diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index a85cd53a1599e2ed6b5447adda7f32589d90fc95..c9bc55685c42493368c9b8fbf37ecb74c973527e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -147,21 +147,33 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a AnalyzedAttributeArguments analyzedArguments = attributeArgumentBinder.BindAttributeArguments(argumentListOpt, attributeTypeForBinding, diagnostics); HashSet useSiteDiagnostics = null; + ImmutableArray argsToParamsOpt = default; + bool expanded = false; + MethodSymbol attributeConstructor = null; // Bind attributeType's constructor based on the bound constructor arguments - MethodSymbol attributeConstructor = attributeTypeForBinding.IsErrorType() ? - null : - BindAttributeConstructor(node, attributeTypeForBinding, analyzedArguments.ConstructorArguments, diagnostics, ref resultKind, suppressErrors: attributeType.IsErrorType(), useSiteDiagnostics: ref useSiteDiagnostics); + if (!attributeTypeForBinding.IsErrorType()) + { + attributeConstructor = BindAttributeConstructor(node, + attributeTypeForBinding, + analyzedArguments.ConstructorArguments, + diagnostics, + ref resultKind, + suppressErrors: attributeType.IsErrorType(), + ref argsToParamsOpt, + ref expanded, + ref useSiteDiagnostics); + } diagnostics.Add(node, useSiteDiagnostics); - if ((object)attributeConstructor != null) + if (!(attributeConstructor is null)) { ReportDiagnosticsIfObsolete(diagnostics, attributeConstructor, node, hasBaseReceiver: false); - } - if (attributeConstructor?.Parameters.Any(p => p.RefKind == RefKind.In) == true) - { - Error(diagnostics, ErrorCode.ERR_AttributeCtorInParameter, node, attributeConstructor.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)); + if (attributeConstructor.Parameters.Any(p => p.RefKind == RefKind.In)) + { + Error(diagnostics, ErrorCode.ERR_AttributeCtorInParameter, node, attributeConstructor.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat)); + } } var constructorArguments = analyzedArguments.ConstructorArguments; @@ -170,10 +182,9 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a ImmutableArray boundNamedArguments = analyzedArguments.NamedArguments; constructorArguments.Free(); - return new BoundAttribute(node, attributeConstructor, boundConstructorArguments, boundConstructorArgumentNamesOpt, + return new BoundAttribute(node, attributeConstructor, boundConstructorArguments, boundConstructorArgumentNamesOpt, argsToParamsOpt, expanded, boundNamedArguments, resultKind, attributeType, hasErrors: resultKind != LookupResultKind.Viable); } - private CSharpAttributeData GetAttribute(BoundAttribute boundAttribute, DiagnosticBag diagnostics) { var attributeType = (NamedTypeSymbol)boundAttribute.Type; @@ -181,6 +192,8 @@ private CSharpAttributeData GetAttribute(BoundAttribute boundAttribute, Diagnost Debug.Assert((object)attributeType != null); + NullableWalker.AnalyzeIfNeeded(Compilation, boundAttribute, diagnostics); + bool hasErrors = boundAttribute.HasAnyErrors; if (attributeType.IsErrorType() || attributeType.IsAbstract || (object)attributeConstructor == null) @@ -484,13 +497,15 @@ private TypeSymbol BindNamedAttributeArgumentType(AttributeArgumentSyntax namedA return namedArgumentType; } - protected virtual MethodSymbol BindAttributeConstructor( + protected MethodSymbol BindAttributeConstructor( AttributeSyntax node, NamedTypeSymbol attributeType, AnalyzedArguments boundConstructorArguments, DiagnosticBag diagnostics, ref LookupResultKind resultKind, bool suppressErrors, + ref ImmutableArray argsToParamsOpt, + ref bool expanded, ref HashSet useSiteDiagnostics) { MemberResolutionResult memberResolutionResult; @@ -511,7 +526,8 @@ private TypeSymbol BindNamedAttributeArgumentType(AttributeArgumentSyntax namedA LookupResultKind.Inaccessible : LookupResultKind.OverloadResolutionFailure); } - + argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; + expanded = memberResolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; return memberResolutionResult.Member; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index f21ec64d9add23386cc9fb768950595b5492e2d0..c220b47f3cb1a55bdb99282d6b0bcbea41808c31 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1438,6 +1438,8 @@ + + diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 4827fd4cca209ae61c15c6f09edd451e7da9ec5b..e1c98e54042474447f80a8d054a7d35fe3f68645 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -423,8 +423,9 @@ protected ParameterSymbol MethodThisParameter { get { - var method = _symbol as MethodSymbol; - return (object)method == null ? null : method.ThisParameter; + ParameterSymbol thisParameter = null; + (_symbol as MethodSymbol)?.TryGetThisParameter(out thisParameter); + return thisParameter; } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index c628a33140637b7fe8bdc38f2ef62a2b5aad44e6..1376047e2e25e003572c5d512a59611d318c6b8f 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -265,7 +265,7 @@ protected override ImmutableArray Scan(ref bool badRegion) this.State = TopState(); // entry point is reachable this.regionPlace = RegionPlace.Before; EnterParameters(); // with parameters assigned - if ((object)methodThisParameter != null) + if (!(methodThisParameter is null)) { EnterParameter(methodThisParameter, methodThisParameter.Type); } @@ -288,6 +288,19 @@ protected override ImmutableArray Scan(ref bool badRegion) Analyze(compilation, method, node, diagnostics, useMethodSignatureReturnType: false, useMethodSignatureParameterTypes: false, methodSignatureOpt: null, returnTypes: null, initialState: null, callbackOpt); } + internal static void AnalyzeIfNeeded( + CSharpCompilation compilation, + BoundAttribute attribute, + DiagnosticBag diagnostics) + { + if (compilation.LanguageVersion < MessageID.IDS_FeatureNullableReferenceTypes.RequiredVersion()) + { + return; + } + + Analyze(compilation, null, attribute, diagnostics, useMethodSignatureReturnType: false, useMethodSignatureParameterTypes: false, methodSignatureOpt: null, returnTypes: null, initialState: null, callbackOpt: null); + } + internal static void Analyze( CSharpCompilation compilation, BoundLambda lambda, @@ -1097,7 +1110,13 @@ protected override LocalState ReachableBottomState() private void EnterParameters() { - var methodParameters = ((MethodSymbol)_symbol).Parameters; + var methodSymbol = _symbol as MethodSymbol; + if (methodSymbol is null) + { + return; + } + + var methodParameters = methodSymbol.Parameters; var signatureParameters = _useMethodSignatureParameterTypes ? _methodSignatureOpt.Parameters : methodParameters; Debug.Assert(signatureParameters.Length == methodParameters.Length); int n = methodParameters.Length; @@ -5726,6 +5745,18 @@ public override BoundNode VisitLockStatement(BoundLockStatement node) return null; } + public override BoundNode VisitAttribute(BoundAttribute node) + { + VisitArguments(node, node.ConstructorArguments, ImmutableArray.Empty, node.Constructor, argsToParamsOpt: node.ConstructorArgumentsToParamsOpt, expanded: node.ConstructorExpanded); + foreach (var assignment in node.NamedArguments) + { + Visit(assignment); + } + + SetNotNullResult(node); + return null; + } + protected override string Dump(LocalState state) { if (!state.Reachable) diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 612684a795187d78954de9806d0b224507f91d6b..ed831463153e8d4e351d18dbbb041792f947b31d 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -6015,7 +6015,7 @@ protected override BoundExpression ShallowClone() internal sealed partial class BoundAttribute : BoundExpression { - public BoundAttribute(SyntaxNode syntax, MethodSymbol constructor, ImmutableArray constructorArguments, ImmutableArray constructorArgumentNamesOpt, ImmutableArray namedArguments, LookupResultKind resultKind, TypeSymbol type, bool hasErrors = false) + public BoundAttribute(SyntaxNode syntax, MethodSymbol constructor, ImmutableArray constructorArguments, ImmutableArray constructorArgumentNamesOpt, ImmutableArray constructorArgumentsToParamsOpt, bool constructorExpanded, ImmutableArray namedArguments, LookupResultKind resultKind, TypeSymbol type, bool hasErrors = false) : base(BoundKind.Attribute, syntax, type, hasErrors || constructorArguments.HasErrors() || namedArguments.HasErrors()) { @@ -6026,6 +6026,8 @@ public BoundAttribute(SyntaxNode syntax, MethodSymbol constructor, ImmutableArra this.Constructor = constructor; this.ConstructorArguments = constructorArguments; this.ConstructorArgumentNamesOpt = constructorArgumentNamesOpt; + this.ConstructorArgumentsToParamsOpt = constructorArgumentsToParamsOpt; + this.ConstructorExpanded = constructorExpanded; this.NamedArguments = namedArguments; this._ResultKind = resultKind; } @@ -6037,6 +6039,10 @@ public BoundAttribute(SyntaxNode syntax, MethodSymbol constructor, ImmutableArra public ImmutableArray ConstructorArgumentNamesOpt { get; } + public ImmutableArray ConstructorArgumentsToParamsOpt { get; } + + public bool ConstructorExpanded { get; } + public ImmutableArray NamedArguments { get; } private readonly LookupResultKind _ResultKind; @@ -6047,11 +6053,11 @@ public override BoundNode Accept(BoundTreeVisitor visitor) return visitor.VisitAttribute(this); } - public BoundAttribute Update(MethodSymbol constructor, ImmutableArray constructorArguments, ImmutableArray constructorArgumentNamesOpt, ImmutableArray namedArguments, LookupResultKind resultKind, TypeSymbol type) + public BoundAttribute Update(MethodSymbol constructor, ImmutableArray constructorArguments, ImmutableArray constructorArgumentNamesOpt, ImmutableArray constructorArgumentsToParamsOpt, bool constructorExpanded, ImmutableArray namedArguments, LookupResultKind resultKind, TypeSymbol type) { - if (constructor != this.Constructor || constructorArguments != this.ConstructorArguments || constructorArgumentNamesOpt != this.ConstructorArgumentNamesOpt || namedArguments != this.NamedArguments || resultKind != this.ResultKind || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (constructor != this.Constructor || constructorArguments != this.ConstructorArguments || constructorArgumentNamesOpt != this.ConstructorArgumentNamesOpt || constructorArgumentsToParamsOpt != this.ConstructorArgumentsToParamsOpt || constructorExpanded != this.ConstructorExpanded || namedArguments != this.NamedArguments || resultKind != this.ResultKind || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundAttribute(this.Syntax, constructor, constructorArguments, constructorArgumentNamesOpt, namedArguments, resultKind, type, this.HasErrors); + var result = new BoundAttribute(this.Syntax, constructor, constructorArguments, constructorArgumentNamesOpt, constructorArgumentsToParamsOpt, constructorExpanded, namedArguments, resultKind, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -6060,7 +6066,7 @@ public BoundAttribute Update(MethodSymbol constructor, ImmutableArray constructorArguments = (ImmutableArray)this.VisitList(node.ConstructorArguments); ImmutableArray namedArguments = (ImmutableArray)this.VisitList(node.NamedArguments); TypeSymbol type = this.VisitType(node.Type); - return node.Update(node.Constructor, constructorArguments, node.ConstructorArgumentNamesOpt, namedArguments, node.ResultKind, type); + return node.Update(node.Constructor, constructorArguments, node.ConstructorArgumentNamesOpt, node.ConstructorArgumentsToParamsOpt, node.ConstructorExpanded, namedArguments, node.ResultKind, type); } public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { @@ -13541,6 +13547,8 @@ public override TreeDumperNode VisitAttribute(BoundAttribute node, object arg) new TreeDumperNode("constructor", node.Constructor, null), new TreeDumperNode("constructorArguments", null, from x in node.ConstructorArguments select Visit(x, null)), new TreeDumperNode("constructorArgumentNamesOpt", node.ConstructorArgumentNamesOpt, null), + new TreeDumperNode("constructorArgumentsToParamsOpt", node.ConstructorArgumentsToParamsOpt, null), + new TreeDumperNode("constructorExpanded", node.ConstructorExpanded, null), new TreeDumperNode("namedArguments", null, from x in node.NamedArguments select Visit(x, null)), new TreeDumperNode("resultKind", node.ResultKind, null), new TreeDumperNode("type", node.Type, null), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index d3f4bd2c62975c2efe9afd43b1d9fc3f129b382b..781b1b17d9f9d6dda20e1a0cc65ceb1403e641f7 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -4578,8 +4578,11 @@ public class MyAttribute : System.Attribute class C { } "; var comp = CreateCompilation(source, options: TestOptions.DebugDll); - comp.VerifyDiagnostics(); - // Expecting a warning. Issue tracked by https://github.com/dotnet/roslyn/issues/23697 + comp.VerifyDiagnostics( + // (7,20): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [My(new string[] { null })] + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 20) + ); } [Fact, WorkItem(31740, "https://github.com/dotnet/roslyn/issues/31740")] @@ -4626,6 +4629,893 @@ class C { } ); } + [Fact] + public void AttributeArgument_Constructor_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string s) { } +} + +[MyAttribute(null)] //1 +class C { } + +[MyAttribute(""str"")] +class D { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 14) + ); + } + + [Fact] + public void AttributeArgument_Constructor_NullLiteral_Suppressed() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string s) { } +} + +[MyAttribute(null!)] +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void AttributeArgument_Constructor_NullLiteral_CSharp7_3() + { + var source = +@" +class MyAttribute : System.Attribute +{ + public MyAttribute(string s) { } +} + +[MyAttribute(null)] +class C { } + +[MyAttribute(""str"")] +class D { } +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular7_3); + comp.VerifyDiagnostics(); + } + + [Fact] + public void AttributeArgument_Constructor_NullLiteral_WithDefaultArgument() + { + var source = +@"#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string s, string s2 = ""str"") { } +} + +[MyAttribute(null)] //1 +class C { } + +[MyAttribute(null, null)] // 2, 3 +class D { } + +[MyAttribute(null, ""str"")] // 4 +class E { } + +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 14), + // (10,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, null)] // 2, 3 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 14), + // (10,20): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, null)] // 2, 3 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 20), + // (13,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, "str")] // 4 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(13, 14) + ); + } + + [Fact] + public void AttributeArgument_Constructor_NullLiteral_WithNullDefaultArgument() + { + var source = +@"#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string s, string s2 = null) { } //1 +} + +[MyAttribute(""str"")] +class C { } + +[MyAttribute(""str"", null)] // 2 +class D { } + +[MyAttribute(""str"", ""str"")] +class E { } + +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,46): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // public MyAttribute(string s, string s2 = null) { } //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 46), + // (10,21): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute("str", null)] // 2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 21) + ); + } + + [Fact] + public void AttributeArgument_Constructor_NullLiteral_WithNamedArguments() + { + var source = +@"#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string s, string s2 = ""str"", string? s3 = ""str"") { } +} + +[MyAttribute(""str"", s2: null, s3: null)] //1 +class C { } + +[MyAttribute(s3: null, s2: null, s: ""str"")] // 2 +class D { } + +[MyAttribute(s3: null, s2: ""str"", s: ""str"")] +class E { } + +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,25): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute("str", s2: null, s3: null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 25), + // (10,28): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(s3: null, s2: null, s: "str")] // 2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 28) + ); + } + + [Fact] + public void AttributeArgument_Constructor_NullLiteral_DisabledEnabled() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string s, string s2) { } +} + +[MyAttribute(null, //1 +#nullable disable +null +#nullable enable +)] +class C { } + +[MyAttribute( +#nullable disable +null, +#nullable enable +null //2 +)] +class D { } + +[MyAttribute(null, //3 +s2: +#nullable disable +null +#nullable enable +)] +class E { } + +[MyAttribute( +#nullable disable +null, +s2: +#nullable enable +null //4 +)] +class F { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 14), + // (19,1): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // null //2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(19, 1), + // (23,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, //3 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(23, 14), + // (36,1): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // null //4 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(36, 1) + ); + } + + [Fact] + public void AttributeArgument_Constructor_NullLiteral_WarningDisabledEnabled() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string s, string s2) { } +} +#nullable disable + +#pragma warning enable nullable +[MyAttribute(null, //1 +#pragma warning disable nullable +null +#pragma warning enable nullable +)] +class C { } + +[MyAttribute( +#pragma warning disable nullable +null, +#pragma warning enable nullable +null //2 +)] +class D { } + +[MyAttribute(null, //3 +s2: +#pragma warning disable nullable +null +#pragma warning enable nullable +)] +class E { } + +[MyAttribute( +#pragma warning disable nullable +null, +s2: +#pragma warning enable nullable +null //4 +)] +class F { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 14), + // (21,1): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // null //2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(21, 1), + // (25,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, //3 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(25, 14), + // (38,1): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // null //4 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(38, 1) + ); + } + + [Fact] + public void AttributeArgument_Constructor_Array_LiteralNull() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string[] s) { } +} + +[MyAttribute(null)] //1 +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 14) + ); + } + + [Fact] + public void AttributeArgument_Constructor_Array_LiteralNull_Suppressed() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string[] s) { } +} + +[MyAttribute(null!)] +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void AttributeArgument_Constructor_Array_ArrayOfNullable() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string[] s) { } +} + +[MyAttribute(new string?[]{ null })] //1 +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,14): warning CS8620: Argument of type 'string?[]' cannot be used as an input of type 'string[]' for parameter 's' in 'MyAttribute.MyAttribute(string[] s)' due to differences in the nullability of reference types. + // [MyAttribute(new string?[]{ null })] //1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "new string?[]{ null }").WithArguments("string?[]", "string[]", "s", "MyAttribute.MyAttribute(string[] s)").WithLocation(8, 14) + ); + } + + [Fact] + public void AttributeArgument_Constructor_Array_ArrayOfNullable_Suppressed() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string[] s) { } +} + +[MyAttribute(new string?[]{ null }!)] +class C { } + +[MyAttribute(new string[]{ null! })] +class D { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void AttributeArgument_Constructor_Array_ArrayOfNullable_ImplicitType() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string[] s) { } +} + +[MyAttribute(new []{ ""str"", null })] //1 +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,14): warning CS8620: Argument of type 'string?[]' cannot be used as an input of type 'string[]' for parameter 's' in 'MyAttribute.MyAttribute(string[] s)' due to differences in the nullability of reference types. + // [MyAttribute(new []{ "str", null })] //1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, @"new []{ ""str"", null }").WithArguments("string?[]", "string[]", "s", "MyAttribute.MyAttribute(string[] s)").WithLocation(8, 14) + ); + } + + [Fact] + public void AttributeArgument_Constructor_Array_NullValueInInitializer() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string[] s) { } +} + +[MyAttribute(new string[]{ ""str"", null, ""str"" })] //1 +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,35): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(new string[]{ "str", null, "str" })] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 35) + ); + } + + [Fact] + public void AttributeArgument_Constructor_Array_NullValueInNestedInitializer() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(object[] s) { } +} + +[MyAttribute(new object[] +{ + new string[] { ""str"", null }, //1 + new string[] { null }, //2 + new string?[] { null } +})] +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,27): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // new string[] { "str", null }, //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 27), + // (11,20): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // new string[] { null }, //2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(11, 20) + ); + } + + [Fact] + public void AttributeArgument_Constructor_ParamsArrayOfNullable_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(params object?[] s) { } +} + +[MyAttribute(null)] //1 +class C { } + +[MyAttribute(null, null)] +class D { } + +[MyAttribute((object?)null)] +class E { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 14) + ); + } + + [Fact] + public void AttributeArgument_Constructor_ParamsArray_NullItem() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute(string s1, params object[] s) { } +} + +[MyAttribute(""str"", null, ""str"", ""str"")] //1 +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,21): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute("str", null, "str", "str")] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 21) + ); + } + + [Fact] + public void AttributeArgument_PropertyAssignment_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute() { } + + public string MyValue { get; set; } = ""str""; +} + +[MyAttribute(MyValue = null)] //1 +class C { } + +[MyAttribute(MyValue = ""str"")] +class D { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,24): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(MyValue = null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 24) + ); + } + + [Fact] + public void AttributeArgument_PropertyAssignment_Array_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute() { } + + public string[] PropertyArray { get; set; } = new string[] { }; + + public string[]? NullablePropertyArray { get; set; } = null; + +} + +[MyAttribute(PropertyArray = null)] //1 +class C { } + +[MyAttribute(NullablePropertyArray = null)] +class D { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (13,30): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(PropertyArray = null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(13, 30) + ); + } + + [Fact] + public void AttributeArgument_PropertyAssignment_Array_ArrayOfNullable() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute() { } + + public string[] PropertyArray { get; set; } = new string[] { ""str"" }; + + public string[]? PropertyNullableArray { get; set; } = new string[] { ""str"" }; +} + +[MyAttribute(PropertyArray = new string?[]{ null })] //1 +class C { } + +[MyAttribute(PropertyNullableArray = null)] +class D { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,30): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // [MyAttribute(PropertyArray = new string?[]{ null })] //1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new string?[]{ null }").WithArguments("string?[]", "string[]").WithLocation(12, 30) + ); + } + + [Fact] + public void AttributeArgument_FieldAssignment_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute() { } + + public string myValue = ""str""; +} + +[MyAttribute(myValue = null)] //1 +class C { } + +[MyAttribute(myValue = ""str"")] +class D { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,24): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(myValue = null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 24) + ); + } + + [Fact] + public void AttributeArgument_FieldAssignment_Array_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute() { } + + public string[] fieldArray = new string[] { }; + + public string[]? nullableFieldArray = null; +} + +[MyAttribute(fieldArray = null)] //1 +class C { } + +[MyAttribute(nullableFieldArray = null)] +class D { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,27): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(fieldArray = null)] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(12, 27) + ); + } + + [Fact] + public void AttributeArgument_FieldAssignment_Array_ArrayOfNullable() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public MyAttribute() { } + + public string[] fieldArray = new string[] { }; + + public string?[] fieldArrayOfNullable = new string?[] { }; +} + +[MyAttribute(fieldArray = new string?[]{ null })] //1 +class C { } + +[MyAttribute(fieldArrayOfNullable = new string?[]{ null })] +class D { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (12,27): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // [MyAttribute(fieldArray = new string?[]{ null })] //1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new string?[]{ null }").WithArguments("string?[]", "string[]").WithLocation(12, 27) + ); + } + + [Fact] + public void AttributeArgument_NoMatchingConstructor_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ +} + +[MyAttribute(null)] //1 +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,2): error CS1729: 'MyAttribute' does not contain a constructor that takes 1 arguments + // [MyAttribute(null)] //1 + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "MyAttribute(null)").WithArguments("MyAttribute", "1").WithLocation(7, 2) + ); + } + + [Fact] + public void AttributeArgument_NoMatchingConstructor_Array_NullValueInInitializer() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ +} + +[MyAttribute(new string[] { null })] //1 +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,2): error CS1729: 'MyAttribute' does not contain a constructor that takes 1 arguments + // [MyAttribute(new string[] { null })] //1 + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "MyAttribute(new string[] { null })").WithArguments("MyAttribute", "1").WithLocation(7, 2), + // (7,29): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(new string[] { null })] //1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 29) + ); + } + + [Fact] + public void AttributeArgument_NoMatchingConstructor_PropertyAssignment_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public string[] PropertyArray { get; set; } = new string[] { ""str"" }; + + public string[]? PropertyNullableArray { get; set; } = new string[] { ""str"" }; +} + +[MyAttribute( // 1 + new string[] { null }, // 2 + PropertyArray = null, // 3 + PropertyNullableArray = new string[] { null } // 4 +)] +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,2): error CS1729: 'MyAttribute' does not contain a constructor that takes 1 arguments + // [MyAttribute( // 1 + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"MyAttribute( // 1 + new string[] { null }, // 2 + PropertyArray = null, // 3 + PropertyNullableArray = new string[] { null } // 4 +)").WithArguments("MyAttribute", "1").WithLocation(10, 2), + // (11,20): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // new string[] { null }, // 2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(11, 20), + // (12,21): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // PropertyArray = null, // 3 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(12, 21), + // (13,44): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // PropertyNullableArray = new string[] { null } // 4 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(13, 44) + ); + } + + [Fact] + public void AttributeArgument_NoMatchingConstructor_FieldAssignment_NullLiteral() + { + var source = +@" +#nullable enable +class MyAttribute : System.Attribute +{ + public string[] fieldArray = new string[] { }; + + public string?[] fieldArrayOfNullable = new string?[] { }; +} + +[MyAttribute( // 1 + new string[] { null }, // 2 + fieldArray = null, // 3 + fieldArrayOfNullable = new string[] { null } // 4 +)] +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,2): error CS1729: 'MyAttribute' does not contain a constructor that takes 1 arguments + // [MyAttribute( // 1 + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"MyAttribute( // 1 + new string[] { null }, // 2 + fieldArray = null, // 3 + fieldArrayOfNullable = new string[] { null } // 4 +)").WithArguments("MyAttribute", "1").WithLocation(10, 2), + // (11,20): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // new string[] { null }, // 2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(11, 20), + // (12,18): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // fieldArray = null, // 3 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(12, 18), + // (13,43): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // fieldArrayOfNullable = new string[] { null } // 4 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(13, 43) + ); + } + + [Fact] + public void AttributeArgument_ComplexAssignment() + { + var source = +@" +#nullable enable +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true)] +class MyAttribute : System.Attribute +{ + public MyAttribute(string s, string s2 = ""str"", string s3 = ""str"") { } + + public string[] fieldArray = new string[] { }; + + public string?[] fieldArrayOfNullable = new string[] { }; + + public string[]? nullableFieldArray = null; + + public string[] PropertyArray { get; set; } = new string[] { }; + + public string?[] PropertyArrayOfNullable { get; set; } = new string[] { }; + + public string[]? NullablePropertyArray { get; set; } = null; + +} + +[MyAttribute(""s1"")] +[MyAttribute(""s1"", s3: ""s3"", fieldArray = new string[]{})] +[MyAttribute(""s1"", s2: ""s2"", fieldArray = new string[]{}, PropertyArray = new string[]{})] +[MyAttribute(""s1"", fieldArrayOfNullable = new string?[]{ null }, NullablePropertyArray = null)] +[MyAttribute(null)] // 1 +[MyAttribute(""s1"", s3: null, fieldArray = new string[]{})] // 2 +[MyAttribute(""s1"", s2: ""s2"", fieldArray = new string?[]{ null }, PropertyArray = new string[]{})] // 3 +[MyAttribute(""s1"", PropertyArrayOfNullable = null)] // 4 +[MyAttribute(""s1"", NullablePropertyArray = new string?[]{ null })] // 5 +[MyAttribute(""s1"", fieldArrayOfNullable = null)] // 6 +[MyAttribute(""s1"", nullableFieldArray = new string[]{ null })] // 7 +[MyAttribute(null, //8 + s2: null, //9 + fieldArrayOfNullable = null, //10 + NullablePropertyArray = new string?[]{ null })] // 11 +[MyAttribute(null, // 12 +#nullable disable + s2: null, +#nullable enable + fieldArrayOfNullable = null, //13 +#pragma warning disable nullable + NullablePropertyArray = new string?[]{ null }, +#pragma warning enable nullable + nullableFieldArray = new string?[]{ null })] //14 +class C { } +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (26,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null)] // 1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(26, 14), + // (27,24): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute("s1", s3: null, fieldArray = new string[]{})] // 2 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(27, 24), + // (28,43): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // [MyAttribute("s1", s2: "s2", fieldArray = new string?[]{ null }, PropertyArray = new string[]{})] // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new string?[]{ null }").WithArguments("string?[]", "string[]").WithLocation(28, 43), + // (29,46): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute("s1", PropertyArrayOfNullable = null)] // 4 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(29, 46), + // (30,44): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // [MyAttribute("s1", NullablePropertyArray = new string?[]{ null })] // 5 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new string?[]{ null }").WithArguments("string?[]", "string[]").WithLocation(30, 44), + // (31,43): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute("s1", fieldArrayOfNullable = null)] // 6 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(31, 43), + // (32,55): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute("s1", nullableFieldArray = new string[]{ null })] // 7 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(32, 55), + // (33,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, //8 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(33, 14), + // (34,17): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // s2: null, //9 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(34, 17), + // (35,36): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // fieldArrayOfNullable = null, //10 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(35, 36), + // (36,37): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // NullablePropertyArray = new string?[]{ null })] // 11 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new string?[]{ null }").WithArguments("string?[]", "string[]").WithLocation(36, 37), + // (37,14): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // [MyAttribute(null, // 12 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(37, 14), + // (41,36): warning CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter. + // fieldArrayOfNullable = null, //13 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(41, 36), + // (45,34): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // nullableFieldArray = new string?[]{ null })] //14 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "new string?[]{ null }").WithArguments("string?[]", "string[]").WithLocation(45, 34) + ); + } + [Fact] public void NullableAndConditionalOperators() {