未验证 提交 9bb1b37c 编写于 作者: J Julien Couvreur 提交者: GitHub

MaybeNullWhen and NotNullWhen attribute affects callers (#36172)

上级 611578d7
......@@ -35,12 +35,12 @@ namespace System.Runtime.CompilerServices
public sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte b)
public NullableAttribute(byte b)
{
NullableFlags = new byte[] { b };
}
public NullableAttribute(byte[] b)
{
NullableFlags = b;
......@@ -52,14 +52,14 @@ namespace System.Runtime.CompilerServices
Each type reference is accompanied by a NullableAttribute with an array of bytes, where 0 is Oblivious, 1 is NotAnnotated and 2 is Annotated.
All value types are marked with flag 0 (oblivious).
To optimize trivial cases the attribute can be omitted, or instead can be replaced with an attribute that takes a single byte value rather than an array.
To optimize trivial cases the attribute can be omitted, or instead can be replaced with an attribute that takes a single byte value rather than an array.
Trivial/optimized cases:
1) All parts are NotAnnotated – a NullableAttribute with a single value 1 (rather than an array of 1s)
2) All parts are Annotated - a NullableAttribute with a single value 2 (rather than an array of 2s)
3) All parts are Oblivious – the attribute is omitted, this matches how we interpret the lack of an attribute in legacy assemblies.
For completeness, we would also recognize a NullableAttribute with a single value 0 (rather than an array of 0s),
but compiler will never emit an attribute like this.
but compiler will never emit an attribute like this.
NullableAttribute(1) should be placed on a type parameter definition that has a `notnull` constraint.
NullableAttribute(1) should be placed on a type parameter definition that has a `class!` constraint.
......@@ -103,9 +103,11 @@ A number of null checks affect the flow state when tested for:
- `is` operator: `x is null`, `x is K` (where `K` is a constant), `x is string`, `x is string s`
Invocation of methods annotated with the following attributes will also affect flow analysis:
- `[NotNullWhenTrue]` (e.g. `TryGetValue`) and `[NotNullWhenFalse]` (e.g. `string.IsNullOrEmpty`)
- `[EnsuresNotNull]` (e.g. `ThrowIfNull`)
- simple pre-conditions: `[AllowNull]` and `[DisallowNull]`
- simple post-conditions: `[MaybeNull]` and `[NotNull]`
- conditional post-conditions: `[MaybeNullWhen(bool)]` and `[NotNullWhen(bool)]`
- `[AssertsTrue]` (e.g. `Debug.Assert`) and `[AssertsFalse]`
See https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-05-15.md
## `default`
If `T` is a reference type, `default(T)` is `T?`.
......
......@@ -351,7 +351,7 @@ private BoundLambda SuppressIfNeeded(BoundLambda lambda)
public bool HasExplicitlyTypedParameterList { get { return Data.HasExplicitlyTypedParameterList; } }
public int ParameterCount { get { return Data.ParameterCount; } }
public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
=> BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, ref useSiteDiagnostics);
=> BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState?.Clone(), ref useSiteDiagnostics);
public RefKind RefKind(int index) { return Data.RefKind(index); }
public void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics, TypeSymbol targetType) { Data.GenerateAnonymousFunctionConversionError(diagnostics, targetType); }
......
......@@ -51,6 +51,11 @@ internal sealed class VariableState
VariableTypes = variableTypes;
VariableNullableStates = variableNullableStates;
}
internal VariableState Clone()
{
return new VariableState(VariableSlot, VariableBySlot, VariableTypes, VariableNullableStates.Clone());
}
}
/// <summary>
......@@ -195,7 +200,6 @@ private TypeWithState ResultType
private void SetResultType(BoundExpression expression, TypeWithState type)
{
SetResult(expression, resultType: type, lvalueType: type.ToTypeWithAnnotations());
}
......@@ -1052,8 +1056,7 @@ private void ReportDiagnostic(ErrorCode errorCode, SyntaxNode syntaxNode, params
private void ReportDiagnostic(ErrorCode errorCode, Location location, params object[] arguments)
{
Debug.Assert(ErrorFacts.NullableFlowAnalysisWarnings.Contains(MessageProvider.Instance.GetIdForErrorCode((int)errorCode)));
Debug.Assert(!IsConditionalState);
if (this.State.Reachable && !_disableDiagnostics)
if (IsReachable() && !_disableDiagnostics)
{
Diagnostics.Add(errorCode, location, arguments);
}
......@@ -2302,6 +2305,7 @@ private int LearnFromNullTest(BoundExpression expression, ref LocalState state)
return -1;
}
// We should not blindly strip conversions here. Tracked by https://github.com/dotnet/roslyn/issues/36164
var expressionWithoutConversion = RemoveConversion(expression, includeExplicitConversions: true).expression;
var slot = MakeSlot(expressionWithoutConversion);
return LearnFromNullTest(slot, expressionWithoutConversion.Type, ref state);
......@@ -2841,91 +2845,6 @@ private FlowAnalysisAnnotations GetAnnotations(MethodSymbol method)
method.ReturnTypeAnnotationAttributes;
}
/// <summary>
/// For each argument, get the annotation from the corresponding parameter.
/// </summary>
private ImmutableArray<FlowAnalysisAnnotations> GetAnnotations(int numArguments,
bool expanded, ImmutableArray<ParameterSymbol> parameters, ImmutableArray<int> argsToParamsOpt)
{
ArrayBuilder<FlowAnalysisAnnotations> builder = null;
for (int i = 0; i < numArguments; i++)
{
(ParameterSymbol parameter, _, FlowAnalysisAnnotations annotations) = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded);
annotations = removeInapplicableAnnotations(parameter, annotations);
if (annotations != FlowAnalysisAnnotations.None && builder == null)
{
builder = ArrayBuilder<FlowAnalysisAnnotations>.GetInstance(numArguments);
builder.AddMany(FlowAnalysisAnnotations.None, i);
}
if (builder != null)
{
builder.Add(annotations);
}
}
return builder == null ? default : builder.ToImmutableAndFree();
FlowAnalysisAnnotations removeInapplicableAnnotations(ParameterSymbol parameter, FlowAnalysisAnnotations annotations)
{
// Ignore NotNullWhenTrue that is inapplicable
annotations = removeInapplicableNotNullWhenSense(parameter, annotations, sense: true);
// Ignore NotNullWhenFalse that is inapplicable
annotations = removeInapplicableNotNullWhenSense(parameter, annotations, sense: false);
const FlowAnalysisAnnotations both = FlowAnalysisAnnotations.AssertsTrue | FlowAnalysisAnnotations.AssertsFalse;
if (parameter?.Type.SpecialType != SpecialType.System_Boolean)
{
// AssertsTrue and AssertsFalse must be applied to a bool parameter
annotations &= ~both;
}
else if ((annotations & both) == both)
{
// We'll ignore AssertsTrue and AssertsFalse if both set
annotations &= ~both;
}
return annotations;
}
FlowAnalysisAnnotations removeInapplicableNotNullWhenSense(ParameterSymbol parameter, FlowAnalysisAnnotations annotations, bool sense)
{
if (parameter is null)
{
return annotations;
}
var whenSense = sense ? FlowAnalysisAnnotations.NotNullWhenTrue : FlowAnalysisAnnotations.NotNullWhenFalse;
var whenNotSense = sense ? FlowAnalysisAnnotations.NotNullWhenFalse : FlowAnalysisAnnotations.NotNullWhenTrue;
// NotNullWhenSense (without NotNullWhenNotSense) must be applied on a bool-returning member
if ((annotations & whenSense) != 0 &&
(annotations & whenNotSense) == 0 &&
parameter.ContainingSymbol.GetTypeOrReturnType().SpecialType != SpecialType.System_Boolean)
{
annotations &= ~whenSense;
}
// NotNullWhenSense must be applied to a reference type, a nullable value type, or an unconstrained generic type
if ((annotations & whenSense) != 0 && !parameter.Type.CanContainNull())
{
annotations &= ~whenSense;
}
// NotNullWhenSense is inapplicable when argument corresponds to params parameter and we're in expanded form
if ((annotations & whenSense) != 0 && expanded && ReferenceEquals(parameter, parameters.Last()))
{
annotations &= ~whenSense;
}
return annotations;
}
}
private static TypeWithAnnotations ApplyLValueAnnotations(TypeWithAnnotations declaredType, FlowAnalysisAnnotations flowAnalysisAnnotations)
{
if ((flowAnalysisAnnotations & FlowAnalysisAnnotations.DisallowNull) == FlowAnalysisAnnotations.DisallowNull)
......@@ -3022,13 +2941,13 @@ protected override void VisitArguments(ImmutableArray<BoundExpression> arguments
MethodSymbol method = null)
{
Debug.Assert(!arguments.IsDefault);
var savedState = this.State.Clone();
(ImmutableArray<BoundExpression> argumentsNoConversions, ImmutableArray<Conversion> conversions) = RemoveArgumentConversions(arguments, refKindsOpt);
// We do a first pass to work through the arguments without making any assumptions
ImmutableArray<VisitArgumentResult> results = VisitArgumentsEvaluate(argumentsNoConversions, refKindsOpt);
// Visit the arguments and collect results
ImmutableArray<VisitArgumentResult> results = VisitArgumentsEvaluate(argumentsNoConversions, refKindsOpt, parameters, argsToParamsOpt, expanded);
// Re-infer method type parameters
if ((object)method != null && method.IsGenericMethod)
{
if (HasImplicitTypeArguments(node))
......@@ -3045,34 +2964,55 @@ protected override void VisitArguments(ImmutableArray<BoundExpression> arguments
if (!node.HasErrors && !parameters.IsDefault)
{
VisitArgumentConversions(arguments, argumentsNoConversions, conversions, refKindsOpt, parameters, argsToParamsOpt, expanded, invokedAsExtensionMethod, results);
}
// We do a second pass through the arguments, ignoring any diagnostics produced, but honoring the annotations,
// to get the proper result state.
ImmutableArray<FlowAnalysisAnnotations> annotations =
GetAnnotations(argumentsNoConversions.Length, expanded, parameters, argsToParamsOpt);
if (!annotations.IsDefault)
{
this.SetState(savedState);
// Visit conversions, inbound assignments including pre-conditions
for (int i = 0; i < argumentsNoConversions.Length; i++)
{
(ParameterSymbol parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations) = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded);
if (parameter is null)
{
continue;
}
bool saveDisableDiagnostics = _disableDiagnostics;
_disableDiagnostics = true;
var argumentNoConversion = argumentsNoConversions[i];
VisitArgumentConversionAndInboundAssignmentsAndPreConditions(
GetConversionIfApplicable(arguments[i], argumentNoConversion),
argumentNoConversion,
conversions.IsDefault ? Conversion.Identity : conversions[i],
GetRefKind(refKindsOpt, i),
parameter,
parameterType,
parameterAnnotations,
results[i],
invokedAsExtensionMethod && i == 0);
}
// Visit outbound assignments and post-conditions
// Note: the state may get split in this step
if (!node.HasErrors && !parameters.IsDefault)
{
// recompute out vars after state was reset
VisitArgumentConversions(arguments, argumentsNoConversions, conversions, refKindsOpt, parameters, argsToParamsOpt, expanded, invokedAsExtensionMethod, results);
}
VisitArgumentsEvaluateHonoringAnnotations(argumentsNoConversions, refKindsOpt, annotations);
for (int i = 0; i < argumentsNoConversions.Length; i++)
{
(ParameterSymbol parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations) = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded);
if (parameter is null)
{
continue;
}
_disableDiagnostics = saveDisableDiagnostics;
VisitArgumentOutboundAssignmentsAndPostConditions(
arguments[i],
GetRefKind(refKindsOpt, i),
parameter,
parameterType,
parameterAnnotations,
results[i]);
}
}
}
return (method, results);
}
private ImmutableArray<VisitArgumentResult> VisitArgumentsEvaluate(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt)
private ImmutableArray<VisitArgumentResult> VisitArgumentsEvaluate(ImmutableArray<BoundExpression> arguments, ImmutableArray<RefKind> refKindsOpt, ImmutableArray<ParameterSymbol> parameters, ImmutableArray<int> argsToParamsOpt, bool expanded)
{
Debug.Assert(!IsConditionalState);
int n = arguments.Length;
......@@ -3083,37 +3023,50 @@ private ImmutableArray<VisitArgumentResult> VisitArgumentsEvaluate(ImmutableArra
var builder = ArrayBuilder<VisitArgumentResult>.GetInstance(n);
for (int i = 0; i < n; i++)
{
builder.Add(VisitArgumentEvaluate(arguments[i], GetRefKind(refKindsOpt, i), preserveConditionalState: false));
FlowAnalysisAnnotations parameterAnnotations = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded).Annotations;
builder.Add(VisitArgumentEvaluate(arguments[i], GetRefKind(refKindsOpt, i), parameterAnnotations));
}
SetInvalidResult();
return builder.ToImmutableAndFree();
}
private VisitArgumentResult VisitArgumentEvaluate(BoundExpression argument, RefKind refKind, bool preserveConditionalState)
private VisitArgumentResult VisitArgumentEvaluate(BoundExpression argument, RefKind refKind, FlowAnalysisAnnotations annotations)
{
Debug.Assert(!IsConditionalState);
var savedState = (argument.Kind == BoundKind.Lambda) ? this.State.Clone() : default(Optional<LocalState>);
// Note: AssertsTrue/AssertsFalse are ineffective on ref/out parameters
switch (refKind)
{
case RefKind.Ref:
Visit(argument);
if (!preserveConditionalState)
{
Unsplit();
}
Unsplit();
break;
case RefKind.None:
case RefKind.In:
if (preserveConditionalState)
{
Visit(argument);
// No Unsplit
UseRvalueOnly(argument); // force use of flow result
}
else
switch (annotations & (FlowAnalysisAnnotations.AssertsTrue | FlowAnalysisAnnotations.AssertsFalse))
{
VisitRvalue(argument);
case FlowAnalysisAnnotations.AssertsTrue:
case FlowAnalysisAnnotations.AssertsTrue | FlowAnalysisAnnotations.AssertsFalse:
Visit(argument);
if (IsConditionalState)
{
SetState(StateWhenTrue);
}
break;
case FlowAnalysisAnnotations.AssertsFalse:
Visit(argument);
if (IsConditionalState)
{
SetState(StateWhenFalse);
}
break;
default:
VisitRvalue(argument);
break;
}
break;
case RefKind.Out:
......@@ -3126,153 +3079,14 @@ private VisitArgumentResult VisitArgumentEvaluate(BoundExpression argument, RefK
break;
}
return new VisitArgumentResult(_visitResult, savedState);
}
/// <summary>
/// Visit all the arguments for the purpose of computing the exit state of the method,
/// given the annotations.
/// If there is any [NotNullWhenTrue/False] annotation, then we'll return in a conditional state for the invocation.
/// </summary>
private void VisitArgumentsEvaluateHonoringAnnotations(
ImmutableArray<BoundExpression> arguments,
ImmutableArray<RefKind> refKindsOpt,
ImmutableArray<FlowAnalysisAnnotations> annotations)
{
Debug.Assert(!IsConditionalState);
Debug.Assert(annotations.Length == arguments.Length);
Debug.Assert(_disableDiagnostics);
for (int i = 0; i < arguments.Length; i++)
{
FlowAnalysisAnnotations annotation = annotations[i];
bool assertsTrue = (annotation & FlowAnalysisAnnotations.AssertsTrue) != 0;
bool assertsFalse = (annotation & FlowAnalysisAnnotations.AssertsFalse) != 0;
if (this.IsConditionalState)
{
// We could be in a conditional state because of a conditional annotation (like NotNullWhenFalse)
// Then WhenTrue/False states correspond to the invocation returning true/false
// We'll first assume that we're in the unconditional state where the method returns true,
// then we'll repeat assuming the method returns false.
LocalState whenTrue = this.StateWhenTrue.Clone();
LocalState whenFalse = this.StateWhenFalse.Clone();
this.SetState(whenTrue);
visitArgumentEvaluateAndUnsplit(i, assertsTrue, assertsFalse);
Debug.Assert(!IsConditionalState);
whenTrue = this.State; // LocalState may be a struct
this.SetState(whenFalse);
visitArgumentEvaluateAndUnsplit(i, assertsTrue, assertsFalse);
Debug.Assert(!IsConditionalState);
whenFalse = this.State; // LocalState may be a struct
this.SetConditionalState(whenTrue, whenFalse);
}
else
{
visitArgumentEvaluateAndUnsplit(i, assertsTrue, assertsFalse);
}
var argument = arguments[i];
var argumentType = argument.Type;
if (!PossiblyNullableType(argumentType))
{
continue;
}
bool notNullWhenTrue = (annotation & FlowAnalysisAnnotations.NotNullWhenTrue) != 0;
bool notNullWhenFalse = (annotation & FlowAnalysisAnnotations.NotNullWhenFalse) != 0;
if (notNullWhenTrue || notNullWhenFalse)
{
// The WhenTrue/False states correspond to the invocation returning true/false
bool wasPreviouslySplit = this.IsConditionalState;
Split();
var slotBuilder = ArrayBuilder<int>.GetInstance();
GetSlotsToMarkAsNotNullable(arguments[i], slotBuilder);
if (notNullWhenTrue)
{
MarkSlotsAsNotNull(slotBuilder, ref StateWhenTrue);
}
if (notNullWhenFalse)
{
MarkSlotsAsNotNull(slotBuilder, ref StateWhenFalse);
if (notNullWhenTrue && !wasPreviouslySplit) Unsplit();
}
slotBuilder.Free();
}
}
SetInvalidResult();
// Evaluate an argument, potentially producing a split state.
// Then unsplit it based on [AssertsTrue] or [AssertsFalse] attributes, or default Unsplit otherwise.
void visitArgumentEvaluateAndUnsplit(int argumentIndex, bool assertsTrue, bool assertsFalse)
{
Debug.Assert(!IsConditionalState);
VisitArgumentEvaluate(arguments[argumentIndex], GetRefKind(refKindsOpt, argumentIndex), preserveConditionalState: true);
if (!this.IsConditionalState)
{
return;
}
else if (assertsTrue)
{
this.SetState(this.StateWhenTrue);
}
else if (assertsFalse)
{
this.SetState(this.StateWhenFalse);
}
else
{
this.Unsplit();
}
}
}
private void VisitArgumentConversions(
ImmutableArray<BoundExpression> arguments,
ImmutableArray<BoundExpression> argumentsNoConversions,
ImmutableArray<Conversion> conversions,
ImmutableArray<RefKind> refKindsOpt,
ImmutableArray<ParameterSymbol> parameters,
ImmutableArray<int> argsToParamsOpt,
bool expanded,
bool invokedAsExtensionMethod,
ImmutableArray<VisitArgumentResult> results)
{
for (int i = 0; i < argumentsNoConversions.Length; i++)
{
(ParameterSymbol parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations) = GetCorrespondingParameter(i, parameters, argsToParamsOpt, expanded);
if (parameter is null)
{
continue;
}
var argumentNoConversion = argumentsNoConversions[i];
VisitArgumentConversion(
GetConversionIfApplicable(arguments[i], argumentNoConversion),
argumentNoConversion,
conversions.IsDefault ? Conversion.Identity : conversions[i],
GetRefKind(refKindsOpt, i),
parameter,
parameterType,
parameterAnnotations,
results[i],
invokedAsExtensionMethod && i == 0);
}
return new VisitArgumentResult(_visitResult, savedState);
}
/// <summary>
/// Report warnings for an argument corresponding to a specific parameter.
/// Verifies that an argument's nullability is compatible with its parameter's on the way in.
/// </summary>
private void VisitArgumentConversion(
private void VisitArgumentConversionAndInboundAssignmentsAndPreConditions(
BoundConversion conversionOpt,
BoundExpression argumentNoConversion,
Conversion conversion,
......@@ -3283,16 +3097,17 @@ void visitArgumentEvaluateAndUnsplit(int argumentIndex, bool assertsTrue, bool a
VisitArgumentResult result,
bool extensionMethodThisArgument)
{
Debug.Assert(!this.IsConditionalState);
// Note: we allow for some variance in `in` and `out` cases. Unlike in binding, we're not
// limited by CLR constraints.
var resultType = result.RValueType;
bool reported = false;
switch (refKind)
{
case RefKind.None:
case RefKind.In:
{
// Note: for lambda arguments, they will be converted in the context/state we saved for that argument
SetResultType(argumentNoConversion,
VisitConversion(
conversionOpt: conversionOpt,
......@@ -3310,75 +3125,227 @@ void visitArgumentEvaluateAndUnsplit(int argumentIndex, bool assertsTrue, bool a
}
break;
case RefKind.Ref:
if (!argumentNoConversion.IsSuppressed)
{
if (!argumentNoConversion.IsSuppressed)
var lvalueResultType = result.LValueType;
if (IsNullabilityMismatch(lvalueResultType.Type, parameterType.Type))
{
var lvalueResultType = result.LValueType;
if (IsNullabilityMismatch(lvalueResultType.Type, parameterType.Type))
{
// declared types must match, ignoring top-level nullability
ReportNullabilityMismatchInRefArgument(argumentNoConversion, argumentType: lvalueResultType.Type, parameter, parameterType.Type);
}
else
{
// types match, but state would let a null in
ReportNullableAssignmentIfNecessary(argumentNoConversion, ApplyLValueAnnotations(parameterType, parameterAnnotations), resultType, useLegacyWarnings: false);
}
// declared types must match, ignoring top-level nullability
ReportNullabilityMismatchInRefArgument(argumentNoConversion, argumentType: lvalueResultType.Type, parameter, parameterType.Type);
}
// Check assignment from a fictional value from the parameter to the argument.
var parameterWithState = TypeWithState.Create(parameterType, parameterAnnotations);
if (argumentNoConversion.IsSuppressed)
else
{
parameterWithState = parameterWithState.WithNotNullState();
// types match, but state would let a null in
ReportNullableAssignmentIfNecessary(argumentNoConversion, ApplyLValueAnnotations(parameterType, parameterAnnotations), resultType, useLegacyWarnings: false);
}
}
break;
case RefKind.Out:
break;
default:
throw ExceptionUtilities.UnexpectedValue(refKind);
}
Debug.Assert(!this.IsConditionalState);
}
var parameterValue = new BoundParameter(argumentNoConversion.Syntax, parameter);
/// <summary>
/// Verifies that outbound assignments (from parameter to argument) are safe and
/// tracks those assignments (or learns from post-condition attributes)
/// </summary>
private void VisitArgumentOutboundAssignmentsAndPostConditions(
BoundExpression argument,
RefKind refKind,
ParameterSymbol parameter,
TypeWithAnnotations parameterType,
FlowAnalysisAnnotations parameterAnnotations,
VisitArgumentResult result)
{
// Note: the state may be conditional if a previous argument involved a conditional post-condition
// The WhenTrue/False states correspond to the invocation returning true/false
switch (refKind)
{
case RefKind.None:
case RefKind.In:
{
// learn from post-conditions [Maybe/NotNull, Maybe/NotNullWhen] without using an assignment
learnFromPostConditions(argument, parameterAnnotations);
}
break;
case RefKind.Ref:
{
// assign from a fictional value from the parameter to the argument.
var parameterWithState = TypeWithState.Create(parameterType, parameterAnnotations);
var parameterValue = new BoundParameter(argument.Syntax, parameter);
var lValueType = result.LValueType;
TrackNullableStateForAssignment(parameterValue, lValueType, MakeSlot(argumentNoConversion), parameterWithState);
trackNullableStateForAssignment(parameterValue, lValueType, MakeSlot(argument), parameterWithState, argument.IsSuppressed, parameterAnnotations);
// check whether parameter would unsafely let a null out
ReportNullableAssignmentIfNecessary(parameterValue, lValueType, parameterWithState, useLegacyWarnings: false);
// check whether parameter would unsafely let a null out in the worse case
if (!argument.IsSuppressed)
{
ReportNullableAssignmentIfNecessary(parameterValue, lValueType, applyPostConditionsUnconditionally(parameterWithState, parameterAnnotations), UseLegacyWarnings(argument, result.LValueType));
}
}
break;
case RefKind.Out:
{
var lValueType = result.LValueType;
var parameterWithAnnotations = ApplyRValueAnnotations(parameterType, parameterAnnotations);
var parameterWithState = TypeWithState.Create(parameterWithAnnotations, parameterAnnotations);
if (argumentNoConversion is BoundLocal local && local.DeclarationKind == BoundLocalDeclarationKind.WithInferredType)
if (argument is BoundLocal local && local.DeclarationKind == BoundLocalDeclarationKind.WithInferredType)
{
_variableTypes[local.LocalSymbol] = parameterWithAnnotations;
lValueType = parameterWithAnnotations;
}
// Check assignment from a fictional value from the parameter to the argument.
var parameterValue = new BoundParameter(argumentNoConversion.Syntax, parameter);
// assign from a fictional value from the parameter to the argument.
var parameterWithState = TypeWithState.Create(parameterWithAnnotations, parameterAnnotations);
var parameterValue = new BoundParameter(argument.Syntax, parameter);
trackNullableStateForAssignment(parameterValue, lValueType, MakeSlot(argument), parameterWithState, argument.IsSuppressed, parameterAnnotations);
if (!argumentNoConversion.IsSuppressed && !reported)
// check whether parameter would unsafely let a null out in the worse case
if (!argument.IsSuppressed)
{
ReportNullableAssignmentIfNecessary(parameterValue, lValueType, parameterWithState, useLegacyWarnings: UseLegacyWarnings(argumentNoConversion, result.LValueType));
ReportNullableAssignmentIfNecessary(parameterValue, lValueType, applyPostConditionsUnconditionally(parameterWithState, parameterAnnotations), UseLegacyWarnings(argument, result.LValueType));
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
if (!_conversions.HasIdentityOrImplicitReferenceConversion(parameterType.Type, lValueType.Type, ref useSiteDiagnostics))
{
ReportNullabilityMismatchInArgument(argumentNoConversion.Syntax, lValueType.Type, parameter, parameterType.Type, forOutput: true);
ReportNullabilityMismatchInArgument(argument.Syntax, lValueType.Type, parameter, parameterType.Type, forOutput: true);
}
}
else
{
parameterWithState = parameterWithState.WithNotNullState();
}
// Set nullable state of argument to parameter type.
TrackNullableStateForAssignment(parameterValue, lValueType, MakeSlot(argumentNoConversion), parameterWithState);
SetResultType(argumentNoConversion, parameterWithState);
}
break;
default:
throw ExceptionUtilities.UnexpectedValue(refKind);
}
void trackNullableStateForAssignment(BoundExpression parameterValue, TypeWithAnnotations lValueType, int targetSlot, TypeWithState parameterWithState, bool isSuppressed, FlowAnalysisAnnotations parameterAnnotations)
{
if (!IsConditionalState && !hasConditionalPostCondition(parameterAnnotations))
{
TrackNullableStateForAssignment(parameterValue, lValueType, targetSlot, parameterWithState.WithSuppression(isSuppressed));
}
else
{
Split();
var originalWhenFalse = StateWhenFalse.Clone();
SetState(StateWhenTrue);
// Note: the suppression applies over the post-condition attributes
TrackNullableStateForAssignment(parameterValue, lValueType, targetSlot, applyPostConditionsWhenTrue(parameterWithState, parameterAnnotations).WithSuppression(isSuppressed));
Debug.Assert(!IsConditionalState);
var newWhenTrue = State.Clone();
SetState(originalWhenFalse);
TrackNullableStateForAssignment(parameterValue, lValueType, targetSlot, applyPostConditionsWhenFalse(parameterWithState, parameterAnnotations).WithSuppression(isSuppressed));
Debug.Assert(!IsConditionalState);
SetConditionalState(newWhenTrue, whenFalse: State);
}
}
static bool hasConditionalPostCondition(FlowAnalysisAnnotations annotations)
{
return (((annotations & FlowAnalysisAnnotations.MaybeNullWhenTrue) != 0) ^ ((annotations & FlowAnalysisAnnotations.MaybeNullWhenFalse) != 0)) ||
(((annotations & FlowAnalysisAnnotations.NotNullWhenTrue) != 0) ^ ((annotations & FlowAnalysisAnnotations.NotNullWhenFalse) != 0));
}
static TypeWithState applyPostConditionsUnconditionally(TypeWithState typeWithState, FlowAnalysisAnnotations annotations)
{
if ((annotations & FlowAnalysisAnnotations.MaybeNull) != 0)
{
// MaybeNull and MaybeNullWhen
return TypeWithState.Create(typeWithState.Type, NullableFlowState.MaybeNull);
}
if ((annotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull)
{
// NotNull
return TypeWithState.Create(typeWithState.Type, NullableFlowState.NotNull);
}
return typeWithState;
}
static TypeWithState applyPostConditionsWhenTrue(TypeWithState typeWithState, FlowAnalysisAnnotations annotations)
{
bool notNullWhenTrue = (annotations & FlowAnalysisAnnotations.NotNullWhenTrue) != 0;
bool maybeNullWhenTrue = (annotations & FlowAnalysisAnnotations.MaybeNullWhenTrue) != 0;
if (maybeNullWhenTrue)
{
return TypeWithState.Create(typeWithState.Type, NullableFlowState.MaybeNull);
}
else if (notNullWhenTrue)
{
return TypeWithState.Create(typeWithState.Type, NullableFlowState.NotNull);
}
return typeWithState;
}
static TypeWithState applyPostConditionsWhenFalse(TypeWithState typeWithState, FlowAnalysisAnnotations annotations)
{
bool notNullWhenFalse = (annotations & FlowAnalysisAnnotations.NotNullWhenFalse) != 0;
bool maybeNullWhenFalse = (annotations & FlowAnalysisAnnotations.MaybeNullWhenFalse) != 0;
if (maybeNullWhenFalse)
{
return TypeWithState.Create(typeWithState.Type, NullableFlowState.MaybeNull);
}
else if (notNullWhenFalse)
{
return TypeWithState.Create(typeWithState.Type, NullableFlowState.NotNull);
}
return typeWithState;
}
void learnFromPostConditions(BoundExpression argument, FlowAnalysisAnnotations parameterAnnotations)
{
// Note: NotNull = NotNullWhen(true) + NotNullWhen(false)
bool notNullWhenTrue = (parameterAnnotations & FlowAnalysisAnnotations.NotNullWhenTrue) != 0;
bool notNullWhenFalse = (parameterAnnotations & FlowAnalysisAnnotations.NotNullWhenFalse) != 0;
// Note: MaybeNull = MaybeNullWhen(true) + MaybeNullWhen(false)
bool maybeNullWhenTrue = (parameterAnnotations & FlowAnalysisAnnotations.MaybeNullWhenTrue) != 0;
bool maybeNullWhenFalse = (parameterAnnotations & FlowAnalysisAnnotations.MaybeNullWhenFalse) != 0;
if (maybeNullWhenTrue && maybeNullWhenFalse && !IsConditionalState && !(notNullWhenTrue && notNullWhenFalse))
{
LearnFromNullTest(argument, ref State);
}
else if (notNullWhenTrue && notNullWhenFalse && !IsConditionalState && !(maybeNullWhenTrue && maybeNullWhenFalse))
{
LearnFromNonNullTest(argument, ref State);
}
else if (notNullWhenTrue || notNullWhenFalse || maybeNullWhenTrue || maybeNullWhenFalse)
{
Split();
if (notNullWhenTrue)
{
LearnFromNonNullTest(argument, ref StateWhenTrue);
}
if (notNullWhenFalse)
{
LearnFromNonNullTest(argument, ref StateWhenFalse);
}
if (maybeNullWhenTrue)
{
LearnFromNullTest(argument, ref StateWhenTrue);
}
if (maybeNullWhenFalse)
{
LearnFromNullTest(argument, ref StateWhenFalse);
}
}
}
}
private (ImmutableArray<BoundExpression> arguments, ImmutableArray<Conversion> conversions) RemoveArgumentConversions(
......@@ -4990,6 +4957,7 @@ private MethodSymbol CheckMethodGroupReceiverNullability(BoundMethodGroup group,
{
arguments.Add(CreatePlaceholderIfNecessary(receiverOpt, receiverType.ToTypeWithAnnotations()));
}
// Create placeholders for the arguments. (See Conversions.GetDelegateArguments()
// which is used for that purpose in initial binding.)
foreach (var parameter in delegateType.DelegateInvokeMethod.Parameters)
......@@ -5231,9 +5199,6 @@ private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> v
}
else
{
// Check nullability for `this` parameter
CheckExtensionMethodThisNullability(right, conversion, deconstructMethod.Parameters[0], rightResult);
if (deconstructMethod.IsGenericMethod)
{
// re-infer the deconstruct parameters based on the 'this' parameter
......@@ -5257,6 +5222,12 @@ private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> v
int offset = invocation.InvokedAsExtensionMethod ? 1 : 0;
Debug.Assert(parameters.Length - offset == n);
if (invocation.InvokedAsExtensionMethod)
{
// Check nullability for `this` parameter
CheckExtensionMethodThisNullability(right, conversion, deconstructMethod.Parameters[0], rightResult);
}
for (int i = 0; i < n; i++)
{
var variable = variables[i];
......@@ -5270,12 +5241,24 @@ private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> v
}
else
{
VisitArgumentConversion(
conversionOpt: null, variable.Expression, underlyingConversion, parameter.RefKind, parameter, parameter.TypeWithAnnotations, GetAnnotations(parameter),
new VisitArgumentResult(new VisitResult(variable.Type.ToTypeWithState(), variable.Type), stateForLambda: default),
VisitArgumentConversionAndInboundAssignmentsAndPreConditions(conversionOpt: null, variable.Expression, underlyingConversion, parameter.RefKind,
parameter, parameter.TypeWithAnnotations, GetAnnotations(parameter), new VisitArgumentResult(new VisitResult(variable.Type.ToTypeWithState(), variable.Type), stateForLambda: default),
extensionMethodThisArgument: false);
}
}
for (int i = 0; i < n; i++)
{
var variable = variables[i];
var parameter = parameters[i + offset];
var nestedVariables = variable.NestedVariables;
if (nestedVariables == null)
{
VisitArgumentOutboundAssignmentsAndPostConditions(
variable.Expression, parameter.RefKind, parameter, parameter.TypeWithAnnotations, GetAnnotations(parameter),
new VisitArgumentResult(new VisitResult(variable.Type.ToTypeWithState(), variable.Type), stateForLambda: default));
}
}
}
}
else
......@@ -6407,7 +6390,7 @@ public override BoundNode VisitArgList(BoundArgList node)
public override BoundNode VisitArgListOperator(BoundArgListOperator node)
{
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt);
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt, parameters: default, argsToParamsOpt: default, expanded: false);
Debug.Assert(node.Type is null);
SetNotNullResult(node);
return null;
......@@ -6491,7 +6474,7 @@ public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node)
CheckPossibleNullReceiver(receiverOpt, receiverType, checkNullableValueType: false);
}
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt);
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt, parameters: default, argsToParamsOpt: default, expanded: false);
Debug.Assert(node.Type.IsDynamic());
Debug.Assert(node.Type.IsReferenceType);
var result = TypeWithAnnotations.Create(node.Type, NullableAnnotation.Oblivious);
......@@ -6522,7 +6505,7 @@ public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjec
{
Debug.Assert(!IsConditionalState);
var arguments = node.Arguments;
var argumentResults = VisitArgumentsEvaluate(arguments, node.ArgumentRefKindsOpt);
var argumentResults = VisitArgumentsEvaluate(arguments, node.ArgumentRefKindsOpt, parameters: default, argsToParamsOpt: default, expanded: false);
VisitObjectOrDynamicObjectCreation(node, arguments, argumentResults, node.InitializerExpressionOpt);
return null;
}
......@@ -6603,7 +6586,7 @@ public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess no
// https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
// after indices have been visited, and only if the receiver has not changed.
_ = CheckPossibleNullReceiver(receiver);
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt);
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt, parameters: default, argsToParamsOpt: default, expanded: false);
Debug.Assert(node.Type.IsDynamic());
var result = TypeWithAnnotations.Create(node.Type, NullableAnnotation.Oblivious);
SetLvalueResultType(node, result);
......@@ -6661,7 +6644,7 @@ private bool ReportPossibleNullReceiverIfNeeded(TypeSymbol type, NullableFlowSta
private void CheckExtensionMethodThisNullability(BoundExpression expr, Conversion conversion, ParameterSymbol parameter, TypeWithState result)
{
VisitArgumentConversion(
VisitArgumentConversionAndInboundAssignmentsAndPreConditions(
conversionOpt: null,
expr,
conversion,
......
......@@ -73,6 +73,8 @@ private TypeWithState(TypeSymbol type, NullableFlowState state)
public TypeWithState WithNotNullState() => new TypeWithState(Type, NullableFlowState.NotNull);
public TypeWithState WithSuppression(bool suppress) => suppress ? new TypeWithState(Type, NullableFlowState.NotNull) : this;
public TypeWithAnnotations ToTypeWithAnnotations()
{
NullableAnnotation annotation = this.State.IsNotNull() || Type?.CanContainNull() == false || Type?.IsTypeParameterDisallowingAnnotation() == true
......
......@@ -16023,12 +16023,12 @@ class CL1
// (27,16): error CS0165: Use of unassigned local variable 'x3'
// M2(ref x3);
Diagnostic(ErrorCode.ERR_UseDefViolation, "x3").WithArguments("x3").WithLocation(27, 16),
// (27,16): warning CS8601: Possible null reference assignment.
// (27,16): warning CS8600: Converting null literal or possible null value to non-nullable type.
// M2(ref x3);
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x3").WithLocation(27, 16),
// (32,16): warning CS8601: Possible null reference assignment.
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x3").WithLocation(27, 16),
// (32,16): warning CS8600: Converting null literal or possible null value to non-nullable type.
// M2(ref x4);
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x4").WithLocation(32, 16),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x4").WithLocation(32, 16),
// (40,16): warning CS8600: Converting null literal or possible null value to non-nullable type.
// M3(out x5);
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x5").WithLocation(40, 16),
......@@ -16931,9 +16931,9 @@ static void G(out object? x, ref object? y, in object? z)
// (5,15): warning CS8600: Converting null literal or possible null value to non-nullable type.
// G(out x, ref y, in z);
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(5, 15),
// (5,22): warning CS8601: Possible null reference assignment.
// (5,22): warning CS8600: Converting null literal or possible null value to non-nullable type.
// G(out x, ref y, in z);
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y").WithLocation(5, 22),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "y").WithLocation(5, 22),
// (6,9): warning CS8602: Dereference of a possibly null reference.
// x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(6, 9),
......@@ -17772,6 +17772,1151 @@ public void SimpleWhere()
);
}
[Fact]
public void MaybeNullWhenTrue_Simple()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s)
{
if (IsNull(s))
{
s.ToString(); // warn
}
else
{
s.ToString(); // ok
}
s.ToString(); // ok
}
public static bool IsNull([MaybeNullWhen(true)] string s) => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13)
);
}
[Fact]
public void MaybeNullWhenTrue_Indexer()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s)
{
_ = this[s]
? s.ToString() // 1
: s.ToString();
}
public bool this[[MaybeNullWhen(true)] string s] => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (8,15): warning CS8602: Dereference of a possibly null reference.
// ? s.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_OutParameter_MiscTypes()
{
// Warn on misused nullability attributes (F3, F4 and F5)? https://github.com/dotnet/roslyn/issues/36073
var source =
@"using System.Runtime.CompilerServices;
class Program
{
static bool F1<T>(T t, [MaybeNullWhen(true)]out T t2) => throw null!;
static bool F2<T>(T t, [MaybeNullWhen(true)]out T t2) where T : class => throw null!;
static bool F3<T>(T t, [MaybeNullWhen(true)]out T? t2) where T : class => throw null!;
static bool F4<T>(T t, [MaybeNullWhen(true)]out T t2) where T : struct => throw null!;
static bool F5<T>(T? t, [MaybeNullWhen(true)]out T? t2) where T : struct => throw null!;
static void M1<T>(T t1)
{
_ = F1(t1, out var s2)
? s2.ToString() // 1
: s2.ToString(); // 2
}
static void M2<T>(T t2) where T : class
{
_ = F1(t2, out var s3) // 3
? s3.ToString() // 4
: s3.ToString();
_ = F2(t2, out var s4) // 5
? s4.ToString() // 6
: s4.ToString();
_ = F3(t2, out var s5)
? s5.ToString() // 7
: s5.ToString(); // 8
t2 = null; // 9
_ = F1(t2, out var s6)
? s6.ToString() // 10
: s6.ToString(); // 11
_ = F2(t2, out var s7) // 12
? s7.ToString() // 13
: s7.ToString(); // 14
_ = F3(t2, out var s8) // 15
? s8.ToString() // 16
: s8.ToString(); // 17
}
static void M3<T>(T? t3) where T : class
{
_ = F1(t3, out var s9)
? s9.ToString() // 18
: s9.ToString(); // 19
_ = F2(t3, out var s10) // 20
? s10.ToString() // 21
: s10.ToString(); // 22
_ = F3(t3, out var s11) // 23
? s11.ToString() // 24
: s11.ToString(); // 25
if (t3 == null) return;
_ = F1(t3, out var s12) // 26
? s12.ToString() // 27
: s12.ToString();
_ = F2(t3, out var s13) // 28
? s13.ToString() // 29
: s13.ToString();
_ = F3(t3, out var s14)
? s14.ToString() // 30
: s14.ToString(); // 31
}
static void M4<T>(T t4) where T : struct
{
_ = F1(t4, out var s15)
? s15.ToString()
: s15.ToString();
_ = F4(t4, out var s16)
? s16.ToString()
: s16.ToString();
}
static void M5<T>(T? t5) where T : struct
{
_ = F1(t5, out var s17)
? s17.Value // 32
: s17.Value; // 33
_ = F5(t5, out var s18)
? s18.Value // 34
: s18.Value; // 35
if (t5 == null) return;
_ = F1(t5, out var s19)
? s19.Value // 36
: s19.Value; // 37
_ = F5(t5, out var s20)
? s20.Value // 38
: s20.Value; // 39
}
}";
var comp = CreateNullableCompilation(new[] { MaybeNullWhenAttributeDefinition, source });
comp.VerifyDiagnostics(
// (12,15): warning CS8602: Dereference of a possibly null reference.
// ? s2.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(12, 15),
// (13,15): warning CS8602: Dereference of a possibly null reference.
// : s2.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(13, 15),
// (17,24): warning CS8600: Converting null literal or possible null value to non-nullable type.
// _ = F1(t2, out var s3) // 3
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "var s3").WithLocation(17, 24),
// (18,15): warning CS8602: Dereference of a possibly null reference.
// ? s3.ToString() // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s3").WithLocation(18, 15),
// (21,24): warning CS8600: Converting null literal or possible null value to non-nullable type.
// _ = F2(t2, out var s4) // 5
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "var s4").WithLocation(21, 24),
// (22,15): warning CS8602: Dereference of a possibly null reference.
// ? s4.ToString() // 6
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s4").WithLocation(22, 15),
// (26,15): warning CS8602: Dereference of a possibly null reference.
// ? s5.ToString() // 7
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s5").WithLocation(26, 15),
// (27,15): warning CS8602: Dereference of a possibly null reference.
// : s5.ToString(); // 8
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s5").WithLocation(27, 15),
// (29,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// t2 = null; // 9
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(29, 14),
// (31,15): warning CS8602: Dereference of a possibly null reference.
// ? s6.ToString() // 10
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s6").WithLocation(31, 15),
// (32,15): warning CS8602: Dereference of a possibly null reference.
// : s6.ToString(); // 11
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s6").WithLocation(32, 15),
// (34,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, out T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F2(t2, out var s7) // 12
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, out T)", "T", "T?").WithLocation(34, 13),
// (35,15): warning CS8602: Dereference of a possibly null reference.
// ? s7.ToString() // 13
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s7").WithLocation(35, 15),
// (36,15): warning CS8602: Dereference of a possibly null reference.
// : s7.ToString(); // 14
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s7").WithLocation(36, 15),
// (38,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, out T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F3(t2, out var s8) // 15
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, out T?)", "T", "T?").WithLocation(38, 13),
// (39,15): warning CS8602: Dereference of a possibly null reference.
// ? s8.ToString() // 16
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s8").WithLocation(39, 15),
// (40,15): warning CS8602: Dereference of a possibly null reference.
// : s8.ToString(); // 17
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s8").WithLocation(40, 15),
// (45,15): warning CS8602: Dereference of a possibly null reference.
// ? s9.ToString() // 18
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s9").WithLocation(45, 15),
// (46,15): warning CS8602: Dereference of a possibly null reference.
// : s9.ToString(); // 19
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s9").WithLocation(46, 15),
// (48,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, out T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F2(t3, out var s10) // 20
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, out T)", "T", "T?").WithLocation(48, 13),
// (49,15): warning CS8602: Dereference of a possibly null reference.
// ? s10.ToString() // 21
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s10").WithLocation(49, 15),
// (50,15): warning CS8602: Dereference of a possibly null reference.
// : s10.ToString(); // 22
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s10").WithLocation(50, 15),
// (52,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, out T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F3(t3, out var s11) // 23
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, out T?)", "T", "T?").WithLocation(52, 13),
// (53,15): warning CS8602: Dereference of a possibly null reference.
// ? s11.ToString() // 24
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s11").WithLocation(53, 15),
// (54,15): warning CS8602: Dereference of a possibly null reference.
// : s11.ToString(); // 25
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s11").WithLocation(54, 15),
// (57,24): warning CS8600: Converting null literal or possible null value to non-nullable type.
// _ = F1(t3, out var s12) // 26
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "var s12").WithLocation(57, 24),
// (58,15): warning CS8602: Dereference of a possibly null reference.
// ? s12.ToString() // 27
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s12").WithLocation(58, 15),
// (61,24): warning CS8600: Converting null literal or possible null value to non-nullable type.
// _ = F2(t3, out var s13) // 28
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "var s13").WithLocation(61, 24),
// (62,15): warning CS8602: Dereference of a possibly null reference.
// ? s13.ToString() // 29
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s13").WithLocation(62, 15),
// (66,15): warning CS8602: Dereference of a possibly null reference.
// ? s14.ToString() // 30
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s14").WithLocation(66, 15),
// (67,15): warning CS8602: Dereference of a possibly null reference.
// : s14.ToString(); // 31
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s14").WithLocation(67, 15),
// (82,15): warning CS8629: Nullable value type may be null.
// ? s17.Value // 32
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s17").WithLocation(82, 15),
// (83,15): warning CS8629: Nullable value type may be null.
// : s17.Value; // 33
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s17").WithLocation(83, 15),
// (86,15): warning CS8629: Nullable value type may be null.
// ? s18.Value // 34
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s18").WithLocation(86, 15),
// (87,15): warning CS8629: Nullable value type may be null.
// : s18.Value; // 35
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s18").WithLocation(87, 15),
// (91,15): warning CS8629: Nullable value type may be null.
// ? s19.Value // 36
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s19").WithLocation(91, 15),
// (92,15): warning CS8629: Nullable value type may be null.
// : s19.Value; // 37
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s19").WithLocation(92, 15),
// (95,15): warning CS8629: Nullable value type may be null.
// ? s20.Value // 38
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s20").WithLocation(95, 15),
// (96,15): warning CS8629: Nullable value type may be null.
// : s20.Value; // 39
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s20").WithLocation(96, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_FromMetadata()
{
var lib = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public static bool MaybeNullWhenTrue([MaybeNullWhen(true)] string s) => throw null!;
}
", MaybeNullWhenAttributeDefinition });
var c = CreateNullableCompilation(@"
public class D
{
public void Main(string s)
{
_ = C.MaybeNullWhenTrue(s)
? s.ToString() // 1
: s.ToString();
s.ToString();
}
}
", references: new[] { lib.EmitToImageReference() });
c.VerifyDiagnostics(
// (7,15): warning CS8602: Dereference of a possibly null reference.
// ? s.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(7, 15)
);
}
[Fact]
public void MaybeNullWhenFalse_FromMetadata()
{
var lib = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public static bool MaybeNullWhenFalse([MaybeNullWhen(false)] string s) => throw null!;
}
", MaybeNullWhenAttributeDefinition });
var c = CreateNullableCompilation(@"
public class D
{
public void Main(string s)
{
_ = C.MaybeNullWhenFalse(s)
? s.ToString()
: s.ToString(); // 1
}
}
", references: new[] { lib.EmitToImageReference() });
c.VerifyDiagnostics(
// (8,15): warning CS8602: Dereference of a possibly null reference.
// : s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 15)
);
}
[Fact]
public void NotNullWhenTrue_FromMetadata()
{
var lib = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public static bool NotNullWhenTrue([NotNullWhen(true)] string? s) => throw null!;
}
", NotNullWhenAttributeDefinition });
var c = CreateNullableCompilation(@"
public class D
{
public void Main(string? s)
{
_ = C.NotNullWhenTrue(s)
? s.ToString()
: s.ToString(); // 1
}
}
", references: new[] { lib.EmitToImageReference() });
c.VerifyDiagnostics(
// (8,15): warning CS8602: Dereference of a possibly null reference.
// : s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 15)
);
}
[Fact]
public void NotNullWhenFalse_FromMetadata()
{
var lib = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public static bool NotNullWhenFalse([NotNullWhen(false)] string? s) => throw null!;
}
", NotNullWhenAttributeDefinition });
var c = CreateNullableCompilation(@"
public class D
{
public void Main(string? s)
{
_ = C.NotNullWhenFalse(s)
? s.ToString() // 1
: s.ToString();
}
}
", references: new[] { lib.EmitToImageReference() });
c.VerifyDiagnostics(
// (7,15): warning CS8602: Dereference of a possibly null reference.
// ? s.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(7, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_OutParameter()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main()
{
string s;
_ = M(out s) // 1
? s.ToString() // 2
: s.ToString();
}
public static bool M([MaybeNullWhen(true)] out string s) => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (8,19): warning CS8600: Converting null literal or possible null value to non-nullable type.
// _ = M(out s) // 1
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s").WithLocation(8, 19),
// (9,15): warning CS8602: Dereference of a possibly null reference.
// ? s.ToString() // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 15)
);
}
[Fact]
public void MaybeNull_OutParameter_InConditional()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main()
{
string s;
_ = M(out s) // 1
? s.ToString() // 2
: s.ToString(); // 3
}
public static bool M([MaybeNull] out string s) => throw null!;
}
", MaybeNullAttributeDefinition }, options: WithNonNullTypesTrue());
// Warn on misused nullability attributes (M)? https://github.com/dotnet/roslyn/issues/36073
c.VerifyDiagnostics(
// (8,19): warning CS8600: Converting null literal or possible null value to non-nullable type.
// _ = M(out s) // 1
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s").WithLocation(8, 19),
// (9,15): warning CS8602: Dereference of a possibly null reference.
// ? s.ToString() // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 15),
// (10,15): warning CS8602: Dereference of a possibly null reference.
// : s.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(10, 15)
);
}
[Fact]
public void MaybeNull_RefParameter_InConditional()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main()
{
string s = """";
_ = M(ref s) // 1
? s.ToString() // 2
: s.ToString(); // 3
}
public static bool M([MaybeNull] ref string s) => throw null!;
}
", MaybeNullAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (8,19): warning CS8600: Converting null literal or possible null value to non-nullable type.
// _ = M(ref s) // 1
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s").WithLocation(8, 19),
// (9,15): warning CS8602: Dereference of a possibly null reference.
// ? s.ToString() // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 15),
// (10,15): warning CS8602: Dereference of a possibly null reference.
// : s.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(10, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_RefParameter()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main()
{
string s = """";
_ = M(ref s) // 1
? s.ToString() // 2
: s.ToString();
}
public static bool M([MaybeNullWhen(true)] ref string s) => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (8,19): warning CS8600: Converting null literal or possible null value to non-nullable type.
// _ = M(ref s) // 1
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s").WithLocation(8, 19),
// (9,15): warning CS8602: Dereference of a possibly null reference.
// ? s.ToString() // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_OnImplicitConversion()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class A
{
public static implicit operator C(A a) => throw null!;
}
public class C
{
public void Main()
{
A a = new A();
_ = IsNull(a)
? a.ToString() // 1
: a.ToString();
}
public static bool IsNull([MaybeNullWhen(true)] C c) => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
// This warning is correct because we should be able to infer that `a` may be null when `(C)a` may be null
c.VerifyDiagnostics(
// (13,15): warning CS8602: Dereference of a possibly null reference.
// ? a.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(13, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_OnImplicitConversion_ToNullable()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class A
{
public static implicit operator C?(A a) => throw null!;
}
public class C
{
public void Main()
{
A a = new A();
_ = IsNull(a) // 1
? a.ToString()
: a.ToString();
}
public static bool IsNull([MaybeNullWhen(true)] C c) => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
// Unexpected second warning
// We should not blindly strip conversions when learning that a value is null.
// In this case, we shouldn't infer that `a` may be null from the fact that `(C)a` may be null in the when-true branch.
// Tracked by https://github.com/dotnet/roslyn/issues/36164
c.VerifyDiagnostics(
// (12,20): warning CS8604: Possible null reference argument for parameter 'c' in 'bool C.IsNull(C c)'.
// _ = IsNull(a) // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "a").WithArguments("c", "bool C.IsNull(C c)").WithLocation(12, 20),
// (13,15): warning CS8602: Dereference of a possibly null reference.
// ? a.ToString()
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(13, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_OnExplicitConversion()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class A
{
public static explicit operator C(A a) => throw null!;
}
public class C
{
public void Main()
{
A a = new A();
_ = IsNull((C)a)
? a.ToString() // 1
: a.ToString();
}
public static bool IsNull([MaybeNullWhen(true)] C c) => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (13,15): warning CS8602: Dereference of a possibly null reference.
// ? a.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(13, 15)
);
}
[Fact]
public void MaybeNull_OnExplicitConversion()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class A
{
public static explicit operator C(A? a) => throw null!;
}
public class C
{
public void Main()
{
A a = new A();
_ = IsNull((C)a)
? a.ToString()
: a.ToString();
}
public static bool IsNull([MaybeNull] C c) => throw null!;
}
", MaybeNullAttributeDefinition }, options: WithNonNullTypesTrue());
// Both diagnostics are unexpected
// We should not blindly strip conversions when learning that a value is null.
// In this case, we shouldn't infer that `a` may be null from the fact that `(C)a` may be null.
// Tracked by https://github.com/dotnet/roslyn/issues/36164
c.VerifyDiagnostics(
// (13,15): warning CS8602: Dereference of a possibly null reference.
// ? a.ToString()
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(13, 15),
// (14,15): warning CS8602: Dereference of a possibly null reference.
// : a.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(14, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_OnImplicitReferenceConversion()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class A : Base
{
}
public class Base
{
public void Main()
{
A a = new A();
_ = IsNull(a)
? a.ToString() // 1
: a.ToString();
}
public static bool IsNull([MaybeNullWhen(true)] Base b) => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (12,15): warning CS8602: Dereference of a possibly null reference.
// ? a.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(12, 15)
);
}
[Fact]
public void MaybeNullWhenTrue_EqualsTrue()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s)
{
if (IsNull(s) == true)
{
s.ToString(); // warn
}
else
{
s.ToString(); // ok
}
s.ToString(); // ok
}
public static bool IsNull([MaybeNullWhen(true)] string s) => throw null!;
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
// https://github.com/dotnet/roslyn/issues/29855: there should only be one diagnostic
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13),
// (13,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // ok
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(13, 13)
);
}
[Fact]
public void MaybeNullWhenTrue_MaybeNullInitialState()
{
// Warn on misused nullability attributes (warn on IsNull)? https://github.com/dotnet/roslyn/issues/36073
var c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string? s)
{
if (IsNull(s))
{
s.ToString(); // 1
}
else
{
s.ToString(); // 2
}
}
public static bool IsNull([MaybeNullWhen(true)] string? s) => throw null!;
}
", MaybeNullWhenAttributeDefinition });
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13),
// (13,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(13, 13)
);
}
[Fact]
public void MaybeNullWhenTrue_MaybeNullWhenFalseOnSameParameter()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s)
{
if (IsNull(s))
{
s.ToString(); // 1
}
else
{
s.ToString(); // MaybeNullWhen(false) was ignored
}
}
public static bool IsNull([MaybeNullWhen(true), MaybeNullWhen(false)] string s) => throw null!; // 2
}
", MaybeNullWhenAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13),
// (16,53): error CS0579: Duplicate 'MaybeNullWhen' attribute
// public static bool IsNull([MaybeNullWhen(true), MaybeNullWhen(false)] string s) => throw null!; // 2
Diagnostic(ErrorCode.ERR_DuplicateAttribute, "MaybeNullWhen").WithArguments("MaybeNullWhen").WithLocation(16, 53)
);
}
[Fact]
public void MaybeNullWhenTrue_MaybeNullOnSameParameter()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s)
{
if (IsNull(s))
{
s.ToString(); // 1
}
else
{
s.ToString(); // 2
}
}
public static bool IsNull([MaybeNullWhen(true), MaybeNull] string s) => throw null!;
}
", MaybeNullWhenAttributeDefinition, MaybeNullAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13),
// (13,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(13, 13)
);
}
[Fact]
public void MaybeNullWhenTrue_NotNullWhenTrueOnSecondParameter()
{
var c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s)
{
if (M(s, s))
{
s.ToString();
}
else
{
s.ToString();
}
s.ToString();
}
public static bool M([MaybeNullWhen(true)] string s, [NotNullWhen(true)] string s2) => throw null!;
}
", MaybeNullWhenAttributeDefinition, NotNullWhenAttributeDefinition });
// post-condition attributes are applied on arguments left-to-right
c.VerifyDiagnostics();
}
[Fact]
public void NotNullWhenTrue_MaybeNullWhenTrueOnSecondParameter()
{
var c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s)
{
if (M(s, s))
{
s.ToString(); // 1
}
else
{
s.ToString();
}
}
public static bool M([NotNullWhen(true)] string s, [MaybeNullWhen(true)] string s2) => throw null!;
}
", MaybeNullWhenAttributeDefinition, NotNullWhenAttributeDefinition });
// Warn on misused nullability attributes (warn on first parameter of M)? https://github.com/dotnet/roslyn/issues/36073
// post-condition attributes are applied on arguments left-to-right
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13)
);
}
[Fact]
public void NotNullWhenFalse_MaybeNullWhenFalseOnSecondParameter()
{
var c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s)
{
if (M(s, s))
{
s.ToString();
}
else
{
s.ToString(); // 1
}
}
public static bool M([NotNullWhen(false)] string s, [MaybeNullWhen(false)] string s2) => throw null!;
}
", MaybeNullWhenAttributeDefinition, NotNullWhenAttributeDefinition });
// Warn on misused nullability attributes (warn on first parameter of M)? https://github.com/dotnet/roslyn/issues/36073
// post-condition attributes are applied on arguments left-to-right
c.VerifyDiagnostics(
// (13,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(13, 13)
);
}
[Fact]
public void MaybeNullWhenFalse_NotNullWhenFalseOnSecondParameter()
{
var c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string? s)
{
if (M(s, s))
{
s.ToString(); // 1
}
else
{
s.ToString();
}
}
public static bool M([MaybeNullWhen(false)] string? s, [NotNullWhen(false)] string? s2) => throw null!;
}
", MaybeNullWhenAttributeDefinition, NotNullWhenAttributeDefinition });
// Warn on misused nullability attributes (warn on first parameter of M)? https://github.com/dotnet/roslyn/issues/36073
// post-condition attributes are applied on arguments left-to-right
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13)
);
}
[Fact]
public void NotNullWhenTrue_RefParameter()
{
var c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main()
{
string? s = null;
_ = M(ref s)
? s.ToString()
: s.ToString(); // 1
}
public static bool M([NotNullWhen(true)] ref string? s) => throw null!;
}
", NotNullWhenAttributeDefinition });
c.VerifyDiagnostics(
// (10,15): warning CS8602: Dereference of a possibly null reference.
// : s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(10, 15)
);
}
[Fact]
public void NotNullWhenFalse_OutParameter_MiscTypes()
{
// Warn on misused nullability attributes (F2, F4)? https://github.com/dotnet/roslyn/issues/36073
var source =
@"using System.Runtime.CompilerServices;
class Program
{
static bool F1<T>(T t, [NotNullWhen(false)]out T t2) => throw null!;
static bool F2<T>(T t, [NotNullWhen(false)]out T t2) where T : class => throw null!;
static bool F3<T>(T t, [NotNullWhen(false)]out T? t2) where T : class => throw null!;
static bool F4<T>(T t, [NotNullWhen(false)]out T t2) where T : struct => throw null!;
static bool F5<T>(T? t, [NotNullWhen(false)]out T? t2) where T : struct => throw null!;
static void M1<T>(T t1)
{
_ = F1(t1, out var s2)
? s2.ToString() // 1
: s2.ToString();
}
static void M2<T>(T t2) where T : class
{
_ = F1(t2, out var s3)
? s3.ToString()
: s3.ToString();
_ = F2(t2, out var s4)
? s4.ToString()
: s4.ToString();
_ = F3(t2, out var s5)
? s5.ToString() // 2
: s5.ToString();
t2 = null; // 3
_ = F1(t2, out var s6)
? s6.ToString() // 4
: s6.ToString();
_ = F2(t2, out var s7) // 5
? s7.ToString() // 6
: s7.ToString();
_ = F3(t2, out var s8) // 7
? s8.ToString() // 8
: s8.ToString();
}
static void M3<T>(T? t3) where T : class
{
_ = F1(t3, out var s9)
? s9.ToString() // 9
: s9.ToString();
_ = F2(t3, out var s10) // 10
? s10.ToString() // 11
: s10.ToString();
_ = F3(t3, out var s11) // 12
? s11.ToString() // 13
: s11.ToString();
if (t3 == null) return;
_ = F1(t3, out var s12)
? s12.ToString()
: s12.ToString();
_ = F2(t3, out var s13)
? s13.ToString()
: s13.ToString();
_ = F3(t3, out var s14)
? s14.ToString() // 14
: s14.ToString();
}
static void M4<T>(T t4) where T : struct
{
_ = F1(t4, out var s15)
? s15.ToString()
: s15.ToString();
_ = F4(t4, out var s16)
? s16.ToString()
: s16.ToString();
}
static void M5<T>(T? t5) where T : struct
{
_ = F1(t5, out var s17)
? s17.Value // 15
: s17.Value;
_ = F5(t5, out var s18)
? s18.Value // 16
: s18.Value;
if (t5 == null) return;
_ = F1(t5, out var s19)
? s19.Value // 17
: s19.Value;
_ = F5(t5, out var s20)
? s20.Value // 18
: s20.Value;
}
}";
var comp = CreateNullableCompilation(new[] { NotNullWhenAttributeDefinition, source });
comp.VerifyDiagnostics(
// (12,15): warning CS8602: Dereference of a possibly null reference.
// ? s2.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(12, 15),
// (26,15): warning CS8602: Dereference of a possibly null reference.
// ? s5.ToString() // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s5").WithLocation(26, 15),
// (29,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// t2 = null; // 3
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(29, 14),
// (31,15): warning CS8602: Dereference of a possibly null reference.
// ? s6.ToString() // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s6").WithLocation(31, 15),
// (34,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, out T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F2(t2, out var s7) // 5
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, out T)", "T", "T?").WithLocation(34, 13),
// (35,15): warning CS8602: Dereference of a possibly null reference.
// ? s7.ToString() // 6
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s7").WithLocation(35, 15),
// (38,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, out T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F3(t2, out var s8) // 7
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, out T?)", "T", "T?").WithLocation(38, 13),
// (39,15): warning CS8602: Dereference of a possibly null reference.
// ? s8.ToString() // 8
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s8").WithLocation(39, 15),
// (45,15): warning CS8602: Dereference of a possibly null reference.
// ? s9.ToString() // 9
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s9").WithLocation(45, 15),
// (48,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, out T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F2(t3, out var s10) // 10
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, out T)", "T", "T?").WithLocation(48, 13),
// (49,15): warning CS8602: Dereference of a possibly null reference.
// ? s10.ToString() // 11
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s10").WithLocation(49, 15),
// (52,13): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, out T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// _ = F3(t3, out var s11) // 12
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, out T?)", "T", "T?").WithLocation(52, 13),
// (53,15): warning CS8602: Dereference of a possibly null reference.
// ? s11.ToString() // 13
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s11").WithLocation(53, 15),
// (66,15): warning CS8602: Dereference of a possibly null reference.
// ? s14.ToString() // 14
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s14").WithLocation(66, 15),
// (82,15): warning CS8629: Nullable value type may be null.
// ? s17.Value // 15
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s17").WithLocation(82, 15),
// (86,15): warning CS8629: Nullable value type may be null.
// ? s18.Value // 16
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s18").WithLocation(86, 15),
// (91,15): warning CS8629: Nullable value type may be null.
// ? s19.Value // 17
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s19").WithLocation(91, 15),
// (95,15): warning CS8629: Nullable value type may be null.
// ? s20.Value // 18
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "s20").WithLocation(95, 15)
);
}
[Fact]
public void NotNullWhenFalse_OutParameter()
{
var c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main()
{
string? s;
_ = M(out s)
? s.ToString() // 1
: s.ToString();
}
public static bool M([NotNullWhen(false)] out string? s) => throw null!;
}
", NotNullWhenAttributeDefinition });
c.VerifyDiagnostics(
// (9,15): warning CS8602: Dereference of a possibly null reference.
// ? s.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 15)
);
}
[Fact]
[WorkItem(29855, "https://github.com/dotnet/roslyn/issues/29855")]
public void NotNullWhenFalse_EqualsTrue()
......@@ -18248,9 +19393,9 @@ public void Main(string s)
" }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (6,15): warning CS8601: Possible null reference assignment.
// (6,15): warning CS8600: Converting null literal or possible null value to non-nullable type.
// M(ref s); // warn 1
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s").WithLocation(6, 15),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s").WithLocation(6, 15),
// (7,9): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(7, 9)
......@@ -18274,9 +19419,9 @@ public void Main()
" }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (7,15): warning CS8601: Possible null reference assignment.
// (7,15): warning CS8600: Converting null literal or possible null value to non-nullable type.
// M(ref s); // warn 1
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s").WithLocation(7, 15),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s").WithLocation(7, 15),
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 9)
......@@ -18760,9 +19905,9 @@ public void M()
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 9),
// (11,15): warning CS8601: Possible null reference assignment.
// (11,15): warning CS8600: Converting null literal or possible null value to non-nullable type.
// M(ref s2); // 2
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s2").WithLocation(11, 15),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s2").WithLocation(11, 15),
// (12,9): warning CS8602: Dereference of a possibly null reference.
// s2.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(12, 9),
......@@ -18775,6 +19920,40 @@ public void M()
);
}
[Fact]
public void RefParameter_WarningKind()
{
var c = CreateCompilation(@"
class C
{
string field = """";
public void M()
{
string local = """";
M(ref local); // 1, W-warning
M(ref field); // 2
M(ref RefString()); // 3
}
ref string RefString() => throw null!;
void M(ref string? s) => throw null!;
}
", options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (8,15): warning CS8600: Converting null literal or possible null value to non-nullable type.
// M(ref local); // 1, W-warning
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "local").WithLocation(8, 15),
// (10,15): warning CS8601: Possible null reference assignment.
// M(ref field); // 2
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "field").WithLocation(10, 15),
// (12,15): warning CS8601: Possible null reference assignment.
// M(ref RefString()); // 3
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "RefString()").WithLocation(12, 15)
);
}
[Fact]
[WorkItem(26739, "https://github.com/dotnet/roslyn/issues/26739")]
public void RefParameter_TopLevelNullability_AlwaysSet()
......@@ -18897,9 +20076,9 @@ public static class Extension
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 9),
// (11,20): warning CS8601: Possible null reference assignment.
// (11,20): warning CS8600: Converting null literal or possible null value to non-nullable type.
// this.M(ref s2); // 2
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s2").WithLocation(11, 20),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s2").WithLocation(11, 20),
// (12,9): warning CS8602: Dereference of a possibly null reference.
// s2.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(12, 9)
......@@ -20099,6 +21278,33 @@ void Main(bool b)
c.VerifyDiagnostics();
}
[Fact]
public void AssertsTrue_WithAssertsFalse()
{
CSharpCompilation c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
class C
{
void M(string s, string? s2)
{
MyAssert(s == null);
s.ToString(); // 1
MyAssert2(s2 != null);
s2.ToString();
}
void MyAssert([AssertsTrue, AssertsFalse] bool condition) => throw null!;
void MyAssert2([AssertsFalse, AssertsTrue] bool condition) => throw null!;
}
", AssertsTrueAttributeDefinition, AssertsFalseAttributeDefinition });
c.VerifyDiagnostics(
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 9)
);
}
[Fact]
public void AssertsTrue_MethodWithReturnType()
{
......@@ -20599,11 +21805,11 @@ void Main(string? s)
{
if (Method(s, s = null))
{
s.ToString(); // warn 1
s.ToString(); // 1
}
else
{
s.ToString(); // warn 2
s.ToString();
}
}
static bool Method([NotNullWhen(false)] string? s, string? s2) => throw null!;
......@@ -20612,11 +21818,8 @@ void Main(string? s)
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13),
// (13,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(13, 13)
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13)
);
}
......@@ -21247,7 +22450,7 @@ void Main(string? s)
}
else
{
s.ToString(); // warn 2
s.ToString();
}
}
public dynamic MyIsNullOrEmpty([NotNullWhen(false)] string? s) => throw null!;
......@@ -21257,10 +22460,7 @@ void Main(string? s)
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13),
// (13,13): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(13, 13)
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(9, 13)
);
VerifyAnnotations(c, "C.MyIsNullOrEmpty", NotNullWhenFalse);
......@@ -21763,6 +22963,44 @@ static void F(object? x, object? y, object[]? a)
);
}
[Fact]
public void NotNullWhenTrue_WithParams()
{
CSharpCompilation c = CreateNullableCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
static bool NotNull([NotNullWhen(true)] params object?[]? args) => throw null!;
static void F(object? x, object? y, object[]? a)
{
if (NotNull())
a.ToString(); // warn 1
if (NotNull(x, y))
{
x.ToString(); // warn 2
y.ToString(); // warn 3
}
if (NotNull(a))
a.ToString();
}
}
", NotNullWhenAttributeDefinition });
c.VerifyDiagnostics(
// (9,13): warning CS8602: Dereference of a possibly null reference.
// a.ToString(); // warn 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(9, 13),
// (13,13): warning CS8602: Dereference of a possibly null reference.
// x.ToString(); // warn 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(13, 13),
// (14,13): warning CS8602: Dereference of a possibly null reference.
// y.ToString(); // warn 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y").WithLocation(14, 13)
);
}
[Fact]
public void NotNull_WithParamsOnFirstParameter()
{
......@@ -22007,7 +23245,10 @@ void M(string? s1, string? s2)
c.VerifyDiagnostics(
// (7,9): error CS0103: The name 'Missing' does not exist in the current context
// Missing(ThrowIfNull(s1, s2 = s1));
Diagnostic(ErrorCode.ERR_NameNotInContext, "Missing").WithArguments("Missing").WithLocation(7, 9)
Diagnostic(ErrorCode.ERR_NameNotInContext, "Missing").WithArguments("Missing").WithLocation(7, 9),
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s2.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(8, 9)
);
}
......@@ -22021,13 +23262,18 @@ public class C
void M(string? s1, string? s2)
{
ThrowIfNull(s1, s2 = s1);
s2.ToString(); // ok
s1.ToString();
s2.ToString(); // 1
}
public static void ThrowIfNull([NotNull] string? x1, string? x2) => throw null!;
}
", NotNullAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics();
// NotNull is a post-condition so it comes after all the arguments have been evaluated
c.VerifyDiagnostics(
// (9,9): warning CS8602: Dereference of a possibly null reference.
// s2.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(9, 9)
);
}
[Fact]
......@@ -22040,17 +23286,13 @@ public class C
void M(string? s1)
{
ThrowIfNull(s1, s1 = null);
s1.ToString(); // warn
s1.ToString();
}
public static void ThrowIfNull([NotNull] string? x1, string? x2) => throw null!;
}
", NotNullAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s1.ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s1").WithLocation(8, 9)
);
c.VerifyDiagnostics();
}
[Fact]
......@@ -22157,17 +23399,13 @@ class C
void Main(string? s)
{
ThrowIfNull(s, s = null);
s.ToString(); // warn
s.ToString();
}
static void ThrowIfNull([NotNull] string? s, string? s2) => throw null!;
}
", NotNullAttributeDefinition }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics(
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 9)
);
c.VerifyDiagnostics();
}
[Fact]
......@@ -22273,6 +23511,59 @@ static void M1<T>(T t1)
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T)", "T", "T?").WithLocation(26, 9));
}
[Fact]
public void AllowNull_WithMaybeNull()
{
// Warn on misused nullability attributes (AllowNull on type that could be marked with `?`, MaybeNull on an `in` or by-val parameter)? https://github.com/dotnet/roslyn/issues/36073
var source =
@"using System.Runtime.CompilerServices;
class Program
{
static void F0<T>([AllowNull, MaybeNull]T t) { }
static void F1<T>([AllowNull, MaybeNull]ref T t) { }
static void F2<T>([AllowNull, MaybeNull]T t) where T : class { }
static void F3<T>([AllowNull, MaybeNull]T t) where T : struct { }
static void F4<T>([AllowNull, MaybeNull]T? t) where T : class { }
static void F5<T>([AllowNull, MaybeNull]T? t) where T : struct { }
static void F6<T>([AllowNull, MaybeNull]in T t) { }
static void M<T>(string? s1, string s2)
{
F0<string>(s1);
s1.ToString(); // 1
F0<string>(s2);
s2.ToString(); // 2
}
static void M_WithRef<T>(string? s1, string s2)
{
F1<string>(ref s1);
s1.ToString(); // 3
F1<string>(ref s2); // 4
s2.ToString(); // 5
}
}";
var comp = CreateNullableCompilation(new[] { AllowNullAttributeDefinition, MaybeNullAttributeDefinition, source });
comp.VerifyDiagnostics(
// (15,9): warning CS8602: Dereference of a possibly null reference.
// s1.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s1").WithLocation(15, 9),
// (18,9): warning CS8602: Dereference of a possibly null reference.
// s2.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(18, 9),
// (23,9): warning CS8602: Dereference of a possibly null reference.
// s1.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s1").WithLocation(23, 9),
// (25,24): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F1<string>(ref s2); // 4
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s2").WithLocation(25, 24),
// (26,9): warning CS8602: Dereference of a possibly null reference.
// s2.ToString(); // 5
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(26, 9)
);
}
[Fact]
public void AllowNull_02()
{
......@@ -23595,6 +24886,45 @@ class Program
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "C<int?>.F()").WithLocation(20, 13));
}
[Fact]
public void MaybeNull_InOrByValParameter()
{
var c = CreateCompilation(new[] { @"
using System.Runtime.CompilerServices;
public class C
{
public void Main(string s1, string s2)
{
_ = M(s1)
? s1.ToString() // 1
: s1.ToString(); // 2
_ = M(s2)
? s2.ToString() // 3
: s2.ToString(); // 4
}
public static bool M([MaybeNull] in string s) => throw null!;
public static bool M2([MaybeNull] string s) => throw null!;
}
", MaybeNullAttributeDefinition }, options: WithNonNullTypesTrue());
// Warn on misused nullability attributes (warn on parameters of M and M2)? https://github.com/dotnet/roslyn/issues/36073
c.VerifyDiagnostics(
// (8,15): warning CS8602: Dereference of a possibly null reference.
// ? s1.ToString() // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s1").WithLocation(8, 15),
// (9,15): warning CS8602: Dereference of a possibly null reference.
// : s1.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s1").WithLocation(9, 15),
// (12,15): warning CS8602: Dereference of a possibly null reference.
// ? s2.ToString() // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(12, 15),
// (13,15): warning CS8602: Dereference of a possibly null reference.
// : s2.ToString(); // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(13, 15)
);
}
[Fact]
public void MaybeNull_OutParameter_01()
{
......@@ -24178,39 +25508,39 @@ static void M1<T>(T t1)
s6.ToString(); // 7
T? s7 = default!;
F2(t2, ref s7);
s7.ToString(); // 8
F2(t2, ref s7); // 8
s7.ToString(); // 9
T? s8 = default!;
F3(t2, ref s8); // 9
s8.ToString(); // 10
F3(t2, ref s8); // 10
s8.ToString(); // 11
}
static void M3<T>(T? t3) where T : class
{
T? s9 = default!;
F1(t3, ref s9);
s9.ToString(); // 11
s9.ToString(); // 12
T s10 = default!;
F2(t3, ref s10); // 12, 13
s10.ToString(); // 14
F2(t3, ref s10); // 13, 14
s10.ToString(); // 15
T? s11 = default!;
F3(t3, ref s11); // 15
s11.ToString(); // 16
F3(t3, ref s11); // 16
s11.ToString(); // 17
if (t3 == null) return;
T s12 = default!;
F1(t3, ref s12); // 17
s12.ToString(); // 18
F1(t3, ref s12); // 18
s12.ToString(); // 19
T s13 = default!;
F2(t3, ref s13); // 19
s13.ToString(); // 20
F2(t3, ref s13); // 20
s13.ToString(); // 21
T? s14 = default!;
F3(t3, ref s14);
s14.ToString(); // 21
s14.ToString(); // 22
}
static void M4<T>(T t4) where T : struct
{
......@@ -24225,21 +25555,21 @@ static void M1<T>(T t1)
static void M5<T>(T? t5) where T : struct
{
T s17 = default!;
F1(t5, ref s17); // 22
_ = s17.Value; // 23
F1(t5, ref s17); // 23
_ = s17.Value; // 24
T s18 = default!;
F5(t5, ref s18); // 24
_ = s18.Value; // 25
F5(t5, ref s18); // 25
_ = s18.Value; // 26
if (t5 == null) return;
T s19 = default!;
F1(t5, ref s19); // 26
_ = s19.Value; // 27
F1(t5, ref s19); // 27
_ = s19.Value; // 28
T s20 = default!;
F5(t5, ref s20); // 28
_ = s20.Value; // 29
F5(t5, ref s20); // 29
_ = s20.Value; // 30
}
}";
var comp = CreateNullableCompilation(new[] { MaybeNullAttributeDefinition, source });
......@@ -24250,9 +25580,9 @@ static void M1<T>(T t1)
// (19,9): warning CS8602: Dereference of a possibly null reference.
// s3.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s3").WithLocation(19, 9),
// (22,20): warning CS8601: Possible null reference assignment.
// (22,20): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F2(t2, ref s4); // 3
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s4").WithLocation(22, 20),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s4").WithLocation(22, 20),
// (23,9): warning CS8602: Dereference of a possibly null reference.
// s4.ToString(); // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s4").WithLocation(23, 9),
......@@ -24266,73 +25596,73 @@ static void M1<T>(T t1)
// s6.ToString(); // 7
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s6").WithLocation(36, 9),
// (39,9): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, ref T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// F2(t2, ref s7); // 8
// F2(t2, ref s7);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, ref T)", "T", "T?").WithLocation(39, 9),
// (40,9): warning CS8602: Dereference of a possibly null reference.
// s7.ToString(); // 9
// s7.ToString(); // 8
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s7").WithLocation(40, 9),
// (43,9): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, ref T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// F3(t2, ref s8); // 10
// F3(t2, ref s8); // 9
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, ref T?)", "T", "T?").WithLocation(43, 9),
// (44,9): warning CS8602: Dereference of a possibly null reference.
// s8.ToString(); // 11
// s8.ToString(); // 10
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s8").WithLocation(44, 9),
// (50,9): warning CS8602: Dereference of a possibly null reference.
// s9.ToString(); // 12
// s9.ToString(); // 11
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s9").WithLocation(50, 9),
// (53,9): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F2<T>(T, ref T)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// F2(t3, ref s10); // 13, 14
// F2(t3, ref s10); // 12, 13
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F2").WithArguments("Program.F2<T>(T, ref T)", "T", "T?").WithLocation(53, 9),
// (53,20): warning CS8601: Possible null reference assignment.
// F2(t3, ref s10); // 13, 14
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s10").WithLocation(53, 20),
// (53,20): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F2(t3, ref s10); // 12, 13
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s10").WithLocation(53, 20),
// (54,9): warning CS8602: Dereference of a possibly null reference.
// s10.ToString(); // 15
// s10.ToString(); // 14
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s10").WithLocation(54, 9),
// (57,9): warning CS8634: The type 'T?' cannot be used as type parameter 'T' in the generic type or method 'Program.F3<T>(T, ref T?)'. Nullability of type argument 'T?' doesn't match 'class' constraint.
// F3(t3, ref s11); // 16
// F3(t3, ref s11); // 15
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "F3").WithArguments("Program.F3<T>(T, ref T?)", "T", "T?").WithLocation(57, 9),
// (58,9): warning CS8602: Dereference of a possibly null reference.
// s11.ToString(); // 17
// s11.ToString(); // 16
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s11").WithLocation(58, 9),
// (62,20): warning CS8601: Possible null reference assignment.
// F1(t3, ref s12); // 18
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s12").WithLocation(62, 20),
// (62,20): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F1(t3, ref s12); // 17
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s12").WithLocation(62, 20),
// (63,9): warning CS8602: Dereference of a possibly null reference.
// s12.ToString(); // 19
// s12.ToString(); // 18
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s12").WithLocation(63, 9),
// (66,20): warning CS8601: Possible null reference assignment.
// F2(t3, ref s13); // 20
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s13").WithLocation(66, 20),
// (66,20): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F2(t3, ref s13); // 19
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "s13").WithLocation(66, 20),
// (67,9): warning CS8602: Dereference of a possibly null reference.
// s13.ToString(); // 21
// s13.ToString(); // 20
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s13").WithLocation(67, 9),
// (71,9): warning CS8602: Dereference of a possibly null reference.
// s14.ToString(); // 22
// s14.ToString(); // 21
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s14").WithLocation(71, 9),
// (86,9): error CS0411: The type arguments for method 'Program.F1<T>(T, ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// F1(t5, ref s17); // 23
// F1(t5, ref s17); // 22
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1<T>(T, ref T)").WithLocation(86, 9),
// (87,17): error CS1061: 'T' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
// _ = s17.Value; // 24
// _ = s17.Value; // 23
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Value").WithArguments("T", "Value").WithLocation(87, 17),
// (90,20): error CS1503: Argument 2: cannot convert from 'ref T' to 'ref T?'
// F5(t5, ref s18); // 25
// F5(t5, ref s18); // 24
Diagnostic(ErrorCode.ERR_BadArgType, "s18").WithArguments("2", "ref T", "ref T?").WithLocation(90, 20),
// (91,17): error CS1061: 'T' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
// _ = s18.Value; // 26
// _ = s18.Value; // 25
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Value").WithArguments("T", "Value").WithLocation(91, 17),
// (95,9): error CS0411: The type arguments for method 'Program.F1<T>(T, ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
// F1(t5, ref s19); // 27
// F1(t5, ref s19); // 26
Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1<T>(T, ref T)").WithLocation(95, 9),
// (96,17): error CS1061: 'T' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
// _ = s19.Value; // 28
// _ = s19.Value; // 27
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Value").WithArguments("T", "Value").WithLocation(96, 17),
// (99,20): error CS1503: Argument 2: cannot convert from 'ref T' to 'ref T?'
// F5(t5, ref s20); // 29
// F5(t5, ref s20); // 28
Diagnostic(ErrorCode.ERR_BadArgType, "s20").WithArguments("2", "ref T", "ref T?").WithLocation(99, 20),
// (100,17): error CS1061: 'T' does not contain a definition for 'Value' and no accessible extension method 'Value' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)
// _ = s20.Value; // 30
// _ = s20.Value; // 29
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Value").WithArguments("T", "Value").WithLocation(100, 17)
);
}
......@@ -24367,9 +25697,9 @@ static void M()
// (4,51): warning CS8625: Cannot convert null literal to non-nullable reference type.
// static void F1([MaybeNull]ref string t) { t = null; } // 0
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 51),
// (9,16): warning CS8601: Possible null reference assignment.
// (9,16): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F1(ref t1); // 1
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t1").WithLocation(9, 16),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "t1").WithLocation(9, 16),
// (10,9): warning CS8602: Dereference of a possibly null reference.
// t1.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t1").WithLocation(10, 9),
......@@ -24752,6 +26082,56 @@ class Program
comp.VerifyDiagnostics();
}
[Fact, WorkItem(35949, "https://github.com/dotnet/roslyn/issues/35949")]
public void NotNull_Complexity()
{
var source = @"
using System;
using System.Runtime.CompilerServices;
class C
{
C f = null!;
void M(C c)
{
c.f = c;
c.NotNull(
x => x.f.NotNull(
y => y.f.NotNull(
z => z.f.NotNull(
q => q.f.NotNull(
w => w.f.NotNull(
e => e.f.NotNull(
r => r.f.NotNull(
_ =>
{
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
"""".NotNull(s => s);
return """";
}))))))));
}
}
static class Ext
{
public static V NotNull<T, V>([NotNull] this T t, Func<T, V> f) => throw null!;
}
";
var comp = CreateNullableCompilation(new[] { NotNullAttributeDefinition, source });
comp.VerifyDiagnostics();
}
[Fact]
public void NotNull_OutParameter_GenericType()
{
......@@ -31239,6 +32619,87 @@ static void Main()
Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { null }").WithLocation(6, 17));
}
[Fact, WorkItem(36132, "https://github.com/dotnet/roslyn/issues/36132")]
public void ImplicitlyTypedArrayCreation_UsesNullabilitiesOfConvertedTypes()
{
var source = @"
class A { }
class B { public static implicit operator A(B? b) => new A(); }
class C { public static implicit operator A?(C c) => new A(); }
class D
{
void Test(A a, B? b, C c)
{
_ = (new A[] { a, b }) /*T:A![]!*/;
_ = (new A?[] { a, b }) /*T:A?[]!*/;
_ = (new[] { a, b }) /*T:A![]!*/;
_ = (new[] { b, a }) /*T:A![]!*/;
_ = (new A[] { a, c }) /*T:A![]!*/; // 1
_ = (new A?[] { a, c }) /*T:A?[]!*/;
_ = (new[] { a, c }) /*T:A?[]!*/;
_ = (new[] { c, a }) /*T:A?[]!*/;
}
}";
var comp = CreateNullableCompilation(source);
comp.VerifyDiagnostics(
// (15,27): warning CS8601: Possible null reference assignment.
// _ = (new A[] { a, c }) /*T:A![]!*/; // 1
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "c").WithLocation(15, 27)
);
comp.VerifyTypes();
}
[Fact, WorkItem(36132, "https://github.com/dotnet/roslyn/issues/36132")]
public void Ternary_UsesNullabilitiesOfConvertedTypes()
{
var source = @"
class A { }
class B { public static implicit operator A(B? b) => new A(); }
class C { public static implicit operator A?(C c) => new A(); }
class D
{
void Test(A a, B? b, C c, bool cond)
{
_ = (cond ? a : b) /*T:A!*/;
_ = (cond ? b : a) /*T:A!*/;
_ = (cond ? a : c) /*T:A?*/;
_ = (cond ? c : a) /*T:A?*/;
}
}";
var comp = CreateNullableCompilation(source);
comp.VerifyDiagnostics();
comp.VerifyTypes();
}
[Fact, WorkItem(36132, "https://github.com/dotnet/roslyn/issues/36132")]
public void ReturnTypeInference_UsesNullabilitiesOfConvertedTypes()
{
var source = @"
using System;
class A { }
class B { public static implicit operator A(B? b) => new A(); }
class C { public static implicit operator A?(C c) => new A(); }
class D
{
void Test(A a, B? b, C c, bool cond)
{
Func<A> x1 = () => { if (cond) return a; else return b; };
Func<A> x2 = () => { if (cond) return b; else return a; };
Func<A?> x3 = () => { if (cond) return a; else return c; };
Func<A?> x4 = () => { if (cond) return c; else return a; };
}
}";
var comp = CreateNullableCompilation(source);
comp.VerifyDiagnostics();
comp.VerifyTypes();
}
[Fact]
public void ExplicitlyTypedArrayCreation()
{
......@@ -41839,6 +43300,35 @@ void M2(C? c)
);
}
[Fact]
public void Call_LambdaConsidersNonFinalState()
{
var source = @"
using System;
class C
{
void M(string? maybeNull1, string? maybeNull2, string? maybeNull3)
{
M1(() => maybeNull1.Length); // 1
M2(() => maybeNull2.Length, maybeNull2 = """"); // 2
M3(maybeNull3 = """", () => maybeNull3.Length);
}
void M1<T>(Func<T> lambda) => throw null!;
void M1(Func<string> lambda) => throw null!;
void M2<T>(Func<T> lambda, object o) => throw null!;
void M3<T>(object o, Func<T> lambda) => throw null!;
}";
var comp = CreateNullableCompilation(new[] { source });
comp.VerifyDiagnostics(
// (7,18): warning CS8602: Dereference of a possibly null reference.
// M1(() => maybeNull1.Length); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "maybeNull1").WithLocation(7, 18),
// (8,18): warning CS8602: Dereference of a possibly null reference.
// M2(() => maybeNull2.Length, maybeNull2 = ""); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "maybeNull2").WithLocation(8, 18)
);
}
[Fact]
public void Call_LearnFromNullTest()
{
......@@ -41861,6 +43351,33 @@ void M2(C? c)
);
}
[Fact, WorkItem(36132, "https://github.com/dotnet/roslyn/issues/36132")]
public void Call_MethodTypeInferenceUsesNullabilitiesOfConvertedTypes()
{
var source = @"
class A { }
class B { public static implicit operator A(B? b) => new A(); }
class C
{
void Test(A a, B? b)
{
A t1 = M<A>(a, b);
A t2 = M(a, b); // unexpected
}
T M<T>(T t1, T t2) => t2;
}";
var comp = CreateNullableCompilation(source);
// There should be no diagnostic. See https://github.com/dotnet/roslyn/issues/36132
comp.VerifyDiagnostics(
// (10,16): warning CS8600: Converting null literal or possible null value to non-nullable type.
// A t2 = M(a, b); // unexpected
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "M(a, b)").WithLocation(10, 16)
);
}
[Fact]
public void Indexer_LearnFromNullTest()
{
......@@ -46216,9 +47733,9 @@ static void Main()
// (10,15): warning CS8601: Possible null reference assignment.
// F(ref s, ref t);
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s").WithLocation(10, 15),
// (10,22): warning CS8601: Possible null reference assignment.
// (10,22): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F(ref s, ref t);
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(10, 22));
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "t").WithLocation(10, 22));
}
[Fact]
......@@ -46305,9 +47822,9 @@ static void F2()
// (15,22): warning CS8620: Argument of type 'List<string?>' cannot be used for parameter 'b' of type 'List<string>' in 'void C.F(ref List<string> a, ref List<string>? b, ref List<string?> c, ref List<string?>? d)' due to differences in the nullability of reference types.
// F(ref b, ref c, ref d, ref a);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "c").WithArguments("List<string?>", "List<string>", "b", "void C.F(ref List<string> a, ref List<string>? b, ref List<string?> c, ref List<string?>? d)").WithLocation(15, 22),
// (15,22): warning CS8601: Possible null reference assignment.
// (15,22): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F(ref b, ref c, ref d, ref a);
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "c").WithLocation(15, 22),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c").WithLocation(15, 22),
// (15,29): error CS0165: Use of unassigned local variable 'd'
// F(ref b, ref c, ref d, ref a);
Diagnostic(ErrorCode.ERR_UseDefViolation, "d").WithArguments("d").WithLocation(15, 29),
......@@ -46317,9 +47834,9 @@ static void F2()
// (15,36): warning CS8620: Argument of type 'List<string>' cannot be used for parameter 'd' of type 'List<string?>' in 'void C.F(ref List<string> a, ref List<string>? b, ref List<string?> c, ref List<string?>? d)' due to differences in the nullability of reference types.
// F(ref b, ref c, ref d, ref a);
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "a").WithArguments("List<string>", "List<string?>", "d", "void C.F(ref List<string> a, ref List<string>? b, ref List<string?> c, ref List<string?>? d)").WithLocation(15, 36),
// (15,36): warning CS8601: Possible null reference assignment.
// (15,36): warning CS8600: Converting null literal or possible null value to non-nullable type.
// F(ref b, ref c, ref d, ref a);
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a").WithLocation(15, 36),
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "a").WithLocation(15, 36),
// (23,15): error CS0165: Use of unassigned local variable 'b'
// F(ref b!, ref c!, ref d!, ref a!);
Diagnostic(ErrorCode.ERR_UseDefViolation, "b").WithArguments("b").WithLocation(23, 15),
......@@ -96789,9 +98306,9 @@ static void F(Pair<object?, object>? p)
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (12,34): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object>(Pair<object, object> p, out object t, out object u)'.
// (12,34): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object?, object>(Pair<object?, object> p, out object? t, out object u)'.
// (object? x, object? y) = p; // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "p").WithArguments("p", "void E.Deconstruct<object, object>(Pair<object, object> p, out object t, out object u)").WithLocation(12, 34),
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "p").WithArguments("p", "void E.Deconstruct<object?, object>(Pair<object?, object> p, out object? t, out object u)").WithLocation(12, 34),
// (13,9): warning CS8602: Dereference of a possibly null reference.
// x.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(13, 9)
......@@ -96822,9 +98339,9 @@ static void F(Pair<object?, Pair<object, object?>?> p)
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (12,9): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object>(Pair<object, object> p, out object t, out object u)'.
// (12,9): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object?>(Pair<object, object?> p, out object t, out object? u)'.
// (object? x, (object y, object? z)) = p; // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "(object? x, (object y, object? z)) = p").WithArguments("p", "void E.Deconstruct<object, object>(Pair<object, object> p, out object t, out object u)").WithLocation(12, 9),
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "(object? x, (object y, object? z)) = p").WithArguments("p", "void E.Deconstruct<object, object?>(Pair<object, object?> p, out object t, out object? u)").WithLocation(12, 9),
// (13,9): warning CS8602: Dereference of a possibly null reference.
// x.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(13, 9),
......@@ -96969,9 +98486,9 @@ static void F(Pair<object?, Pair2<object, object?>?> p)
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (21,9): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object>(Pair2<object, object> p, out object t, out object u)'.
// (21,9): warning CS8604: Possible null reference argument for parameter 'p' in 'void E.Deconstruct<object, object?>(Pair2<object, object?> p, out object t, out object? u)'.
// var (x, (y, z)) = p; // 1, 2, 3
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "var (x, (y, z)) = p").WithArguments("p", "void E.Deconstruct<object, object>(Pair2<object, object> p, out object t, out object u)").WithLocation(21, 9),
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "var (x, (y, z)) = p").WithArguments("p", "void E.Deconstruct<object, object?>(Pair2<object, object?> p, out object t, out object? u)").WithLocation(21, 9),
// (21,27): warning CS8634: The type 'object?' cannot be used as type parameter 'T' in the generic type or method 'E.Deconstruct<T, U>(Pair<T, U>, out T, out U)'. Nullability of type argument 'object?' doesn't match 'class' constraint.
// var (x, (y, z)) = p; // 1, 2, 3
Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterReferenceTypeConstraint, "p").WithArguments("E.Deconstruct<T, U>(Pair<T, U>, out T, out U)", "T", "object?").WithLocation(21, 27),
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册