From dd21101da5025d35130c8a98a959b822cc9c7d54 Mon Sep 17 00:00:00 2001 From: Julien Date: Thu, 26 May 2016 16:33:13 -0700 Subject: [PATCH] Bug 217740: don't crash when emitting or loading a DateTimeConstant(-1) attribute (#11536) * Bug 217740: Repro and test harness for C# * Testing and fixing loading scenario in C# and VB * Verifying VB case using attribute on field * Adding more tests * Adding VB tests * Adding tests * More tests on fields with double-attributes * New test constraint * Fixing VB side to new test constraints * Fixing C# side to new test constraints * Fixing remaining tests * Adding comments * Fixing VB field scenario too --- .../Source/SourceComplexParameterSymbol.cs | 3 +- .../AttributeTests_WellKnownAttributes.cs | 278 +++++++++++++- .../Test/Emit/Emit/OptionalArgumentsTests.cs | 48 ++- .../Semantics/NamedAndOptionalTests.cs | 12 +- .../Core/Portable/MetadataReader/PEModule.cs | 13 +- .../Symbols/Attributes/CommonAttributeData.cs | 10 +- .../Binding/EarlyWellKnownAttributeBinder.vb | 3 +- .../Symbols/Source/SourceFieldSymbol.vb | 40 +- .../Symbols/Source/SourceParameterSymbol.vb | 3 +- .../AttributeTests_WellKnownAttributes.vb | 344 +++++++++++++++++- .../Test/Emit/Emit/OptionalArgumentsTests.vb | 7 + 11 files changed, 709 insertions(+), 52 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index ac4f9e08b0363..f51abdc030503 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -642,8 +642,7 @@ private void VerifyParamDefaultValueMatchesAttributeIfAny(ConstantValue value, C if (data != null) { var attrValue = data.DefaultParameterValue; - if (!attrValue.IsBad && - (attrValue != ConstantValue.Unset) && + if ((attrValue != ConstantValue.Unset) && (value != attrValue)) { // CS8017: The parameter has multiple distinct default values. diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs index ad7f9138a28a3..661e855b59512 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -416,6 +417,281 @@ public static void Main() {} CompileAndVerify(text, additionalRefs: new[] { SystemRef }, sourceSymbolValidator: attributeValidator); } + [Fact] + [WorkItem(217740, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=217740")] + public void DateTimeConstantAttribute() + { + #region "Source" + var source = @" +using System; +using System.Runtime.CompilerServices; + +public class Bar +{ + public void Method([DateTimeConstant(-1)]DateTime p1) { } +} +"; + #endregion + + // The native C# compiler emits this: + // .param[1] + // .custom instance void[mscorlib] System.Runtime.CompilerServices.DateTimeConstantAttribute::.ctor(int64) = ( + // 01 00 ff ff ff ff ff ff ff ff 00 00 + // ) + Action verifier = (module) => + { + var bar = (NamedTypeSymbol)((ModuleSymbol)module).GlobalNamespace.GetMember("Bar"); + var method = (MethodSymbol)bar.GetMember("Method"); + var parameters = method.GetParameters(); + var theParameter = (PEParameterSymbol)parameters[0]; + var peModule = (PEModuleSymbol)module; + + Assert.Equal(ParameterAttributes.HasDefault, theParameter.Flags); // native compiler has None instead + + // let's find the attribute in the PE metadata + var attributeInfo = PEModule.FindTargetAttribute(peModule.Module.MetadataReader, theParameter.Handle, AttributeDescription.DateTimeConstantAttribute); + Assert.True(attributeInfo.HasValue); + + long attributeValue; + Assert.True(peModule.Module.TryExtractLongValueFromAttribute(attributeInfo.Handle, out attributeValue)); + Assert.Equal(-1L, attributeValue); // check the attribute is constructed with a -1 + + // check .param has no value + var constantValue = peModule.Module.GetParamDefaultValue(theParameter.Handle); + Assert.Equal(ConstantValue.Null, constantValue); + }; + + var comp = CompileAndVerify(source, symbolValidator: verifier); + comp.VerifyDiagnostics(); + } + + [Fact] + [WorkItem(217740, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=217740")] + public void DateTimeConstantAttributeReferencedViaRef() + { + #region "Source" + var source1 = @" +using System; +using System.Runtime.CompilerServices; + +public class Bar +{ + public void Method([DateTimeConstant(-1)]DateTime p1) { } +} +"; + + var source2 = @" +public class Consumer +{ + public static void M() + { + new Bar().Method(); + } +} +"; + #endregion + + var libComp = CreateCompilationWithMscorlib(source1); + var libCompRef = new CSharpCompilationReference(libComp); + + var comp2 = CreateCompilationWithMscorlib(source2, new[] { libCompRef }); + comp2.VerifyDiagnostics( + // (6,19): error CS7036: There is no argument given that corresponds to the required formal parameter 'p1' of 'Bar.Method(DateTime)' + // new Bar().Method(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Method").WithArguments("p1", "Bar.Method(System.DateTime)").WithLocation(6, 19) + ); + + // The native compiler also gives an error: error CS1501: No overload for method 'Method' takes 0 arguments + var libAssemblyRef = libComp.EmitToImageReference(); + var comp3 = CreateCompilationWithMscorlib(source2, new[] { libAssemblyRef }); + comp3.VerifyDiagnostics( + // (6,19): error CS7036: There is no argument given that corresponds to the required formal parameter 'p1' of 'Bar.Method(DateTime)' + // new Bar().Method(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "Method").WithArguments("p1", "Bar.Method(System.DateTime)").WithLocation(6, 19) + ); + } + + [Fact] + [WorkItem(217740, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=217740")] + public void DateTimeConstantAttributeWithBadDefaultValue() + { + #region "Source" + var source = @" +using System; +using System.Runtime.CompilerServices; + +public class Bar +{ + public DateTime M1([DateTimeConstant(-1)] DateTime x = default(DateTime)) { return x; } + public static void Main() + { + Console.WriteLine(new Bar().M1().Ticks); + } +} +"; + #endregion + + // The native C# compiler would succeed and emit this: + // .method public hidebysig instance void M1([opt] valuetype[mscorlib] System.DateTime x) cil managed + // { + // .param [1] = nullref + // .custom instance void[mscorlib] System.Runtime.CompilerServices.DateTimeConstantAttribute::.ctor(int64) = ( 01 00 FF FF FF FF FF FF FF FF 00 00 ) + + var comp = CreateCompilationWithMscorlib(source); + comp.VerifyDiagnostics( + // (7,60): error CS8017: The parameter has multiple distinct default values. + // public DateTime M1([DateTimeConstant(-1)] DateTime x = default(DateTime)) { return x; } + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "default(DateTime)").WithLocation(7, 60) + ); + } + + [Fact] + [WorkItem(217740, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=217740")] + public void DateTimeConstantAttributeWithValidDefaultValue() + { + #region "Source" + var source = @" +using System; +using System.Runtime.CompilerServices; + +public class Bar +{ + public DateTime M1([DateTimeConstant(42)] DateTime x = default(DateTime)) { return x; } + public static void Main() + { + Console.WriteLine(new Bar().M1().Ticks); + } +} +"; + #endregion + + // The native C# compiler emits this: + // .param [1] = nullref + // .custom instance void[mscorlib] System.Runtime.CompilerServices.DateTimeConstantAttribute::.ctor(int64) = (01 00 2A 00 00 00 00 00 00 00 00 00 ) + + var comp = CreateCompilationWithMscorlib(source); + comp.VerifyDiagnostics( + // (7,60): error CS8017: The parameter has multiple distinct default values. + // public DateTime M1([DateTimeConstant(42)] DateTime x = default(DateTime)) { return x; } + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "default(DateTime)").WithLocation(7, 60) + ); + } + + [Fact] + [WorkItem(217740, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=217740")] + public void DateTimeConstantAttributeWithBadDefaultValueOnField() + { + #region "Source" + var source = @" +using System; +using System.Runtime.CompilerServices; + +public class C +{ + [DateTimeConstant(-1)] + public DateTime F = default(DateTime); + + public static void Main() + { + System.Console.WriteLine(new C().F.Ticks); + } +} +"; + #endregion + + // The native C# compiler emits this: + // .field public valuetype[mscorlib] System.DateTime F + // .custom instance void[mscorlib] System.Runtime.CompilerServices.DateTimeConstantAttribute::.ctor(int64) = ( 01 00 FF FF FF FF FF FF FF FF 00 00 ) + + // using the native compiler, this code outputs 0 + var comp = CompileAndVerify(source, expectedOutput: "0"); + comp.VerifyDiagnostics(); + } + + [Fact] + [WorkItem(217740, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=217740")] + public void DateTimeConstantAttributeWithValidDefaultValueOnField() + { + #region "Source" + var source = @" +using System; +using System.Runtime.CompilerServices; + +public class C +{ + [DateTimeConstant(42)] + public DateTime F = default(DateTime); + + public static void Main() + { + System.Console.WriteLine(new C().F.Ticks); + } +} +"; + #endregion + + // The native C# compiler emits this: + // .field public valuetype[mscorlib] System.DateTime F + // .custom instance void[mscorlib] System.Runtime.CompilerServices.DateTimeConstantAttribute::.ctor(int64) = ( 01 00 2A 00 00 00 00 00 00 00 00 00 ) + + // Using the native compiler, the code executes to output 0 + var comp = CompileAndVerify(source, expectedOutput: "0"); + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(217740, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?id=217740")] + public void LoadingDateTimeConstantWithBadValue() + { + var ilsource = @" +.class public auto ansi beforefieldinit C + extends [mscorlib]System.Object +{ + .method public hidebysig instance valuetype [mscorlib]System.DateTime + Method([opt] valuetype [mscorlib]System.DateTime p) cil managed + { + .param [1] + .custom instance void [mscorlib]System.Runtime.CompilerServices.DateTimeConstantAttribute::.ctor(int64) = ( 01 00 FF FF FF FF FF FF FF FF 00 00 ) + // Code size 7 (0x7) + .maxstack 1 + .locals init (valuetype [mscorlib]System.DateTime V_0) + IL_0000: nop + IL_0001: ldarg.1 + IL_0002: stloc.0 + IL_0003: br.s IL_0005 + + IL_0005: ldloc.0 + IL_0006: ret + } // end of method C::Method + + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method C::.ctor + +} // end of class C + +"; + + var cssource = @" +public class D +{ + public static void Main() + { + System.Console.WriteLine(new C().Method().Ticks); + } +} +"; + + var ilReference = CompileIL(ilsource); + CompileAndVerify(cssource, expectedOutput: "0", additionalRefs: new[] { ilReference }); + // The native compiler would produce a working exe, but that exe would fail at runtime + } + [Fact] public void TestDecimalConstantAttribute() { @@ -3176,7 +3452,7 @@ public static int Main () // the resulting code does not need to verify // This is consistent with Dev10 behavior - CompileAndVerify(source, options: TestOptions.ReleaseDll, verify:false, sourceSymbolValidator: sourceValidator, symbolValidator: metadataValidator); + CompileAndVerify(source, options: TestOptions.ReleaseDll, verify: false, sourceSymbolValidator: sourceValidator, symbolValidator: metadataValidator); } [Fact, WorkItem(544507, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544507")] diff --git a/src/Compilers/CSharp/Test/Emit/Emit/OptionalArgumentsTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/OptionalArgumentsTests.cs index 0f6d48385eb95..b707a38867a72 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/OptionalArgumentsTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/OptionalArgumentsTests.cs @@ -252,43 +252,53 @@ interface I CreateCompilationWithMscorlib(source, references: new[] { SystemRef }).VerifyDiagnostics( // (5,14): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute // void F1([DefaultParameterValue(1)]int o = 2); - Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue"), + Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue").WithLocation(5, 14), // (6,14): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute // void F2([DefaultParameterValue(1)]decimal o = 2); - Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue"), + Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue").WithLocation(6, 14), + // (6,51): error CS8017: The parameter has multiple distinct default values. + // void F2([DefaultParameterValue(1)]decimal o = 2); + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "2").WithLocation(6, 51), // (7,57): error CS8017: The parameter has multiple distinct default values. // void F4([DecimalConstant(0, 0, 0, 0, 1)]decimal o = 2); - Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "2"), + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "2").WithLocation(7, 57), // (8,35): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute // void F6([DateTimeConstant(1), DefaultParameterValue(1), DecimalConstant(0, 0, 0, 0, 1)]int o = 1); - Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue"), + Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue").WithLocation(8, 35), // (8,61): error CS8017: The parameter has multiple distinct default values. // void F6([DateTimeConstant(1), DefaultParameterValue(1), DecimalConstant(0, 0, 0, 0, 1)]int o = 1); - Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DecimalConstant(0, 0, 0, 0, 1)"), + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DecimalConstant(0, 0, 0, 0, 1)").WithLocation(8, 61), // (8,100): error CS8017: The parameter has multiple distinct default values. // void F6([DateTimeConstant(1), DefaultParameterValue(1), DecimalConstant(0, 0, 0, 0, 1)]int o = 1); - Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "1"), + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "1").WithLocation(8, 100), // (9,35): error CS8017: The parameter has multiple distinct default values. // void F7([DateTimeConstant(2), DecimalConstant(0, 0, 0, 0, 2), DefaultParameterValue(2)]decimal o = 2); - Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DecimalConstant(0, 0, 0, 0, 2)"), + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DecimalConstant(0, 0, 0, 0, 2)").WithLocation(9, 35), // (9,67): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute // void F7([DateTimeConstant(2), DecimalConstant(0, 0, 0, 0, 2), DefaultParameterValue(2)]decimal o = 2); - Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue"), + Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue").WithLocation(9, 67), // (9,104): error CS8017: The parameter has multiple distinct default values. // void F7([DateTimeConstant(2), DecimalConstant(0, 0, 0, 0, 2), DefaultParameterValue(2)]decimal o = 2); - Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "2"), + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "2").WithLocation(9, 104), // (10,25): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute // object this[int a, [DefaultParameterValue(1)]int o = 2] { get; set; } - Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue"), + Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue").WithLocation(10, 25), + // (10,58): error CS8017: The parameter has multiple distinct default values. + // object this[int a, [DefaultParameterValue(1)]int o = 2] { get; set; } + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "2").WithLocation(10, 58), // (11,44): error CS8017: The parameter has multiple distinct default values. // object this[[DefaultParameterValue(0), DecimalConstant(0, 0, 0, 0, 0), DateTimeConstant(0)]int o] { get; set; } - Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DecimalConstant(0, 0, 0, 0, 0)"), + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DecimalConstant(0, 0, 0, 0, 0)").WithLocation(11, 44), // (11,76): error CS8017: The parameter has multiple distinct default values. // object this[[DefaultParameterValue(0), DecimalConstant(0, 0, 0, 0, 0), DateTimeConstant(0)]int o] { get; set; } - Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DateTimeConstant(0)"), + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DateTimeConstant(0)").WithLocation(11, 76), + // (5,47): error CS8017: The parameter has multiple distinct default values. + // void F1([DefaultParameterValue(1)]int o = 2); + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "2").WithLocation(5, 47), // (13,61): error CS8017: The parameter has multiple distinct default values. // delegate void D([DecimalConstant(0, 0, 0, 0, 3)]decimal b = 4); - Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "4")); + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "4").WithLocation(13, 61) + ); } [WorkItem(529684, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529684")] @@ -334,14 +344,22 @@ interface I { void M1([DefaultParameterValue(typeof(C)), DecimalConstantAttribute(0, 0, 0, 0, 0)] decimal o); void M2([DefaultParameterValue(0), DecimalConstantAttribute(0, 0, 0, 0, typeof(C))] decimal o); + void M3([DefaultParameterValue(0), DecimalConstantAttribute(0, 0, 0, 0, 0)] decimal o); }"; CreateCompilationWithMscorlib(source, references: new[] { SystemRef }).VerifyDiagnostics( + // (7,40): error CS8017: The parameter has multiple distinct default values. + // void M3([DefaultParameterValue(0), DecimalConstantAttribute(0, 0, 0, 0, 0)] decimal o); + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DecimalConstantAttribute(0, 0, 0, 0, 0)").WithLocation(7, 40), // (6,84): error CS0246: The type or namespace name 'C' could not be found (are you missing a using directive or an assembly reference?) // void M2([DefaultParameterValue(0), DecimalConstantAttribute(0, 0, 0, 0, typeof(C))] decimal o); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "C").WithArguments("C"), + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "C").WithArguments("C").WithLocation(6, 84), // (5,43): error CS0246: The type or namespace name 'C' could not be found (are you missing a using directive or an assembly reference?) // void M1([DefaultParameterValue(typeof(C)), DecimalConstantAttribute(0, 0, 0, 0, 0)] decimal o); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "C").WithArguments("C")); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "C").WithArguments("C").WithLocation(5, 43), + // (5,48): error CS8017: The parameter has multiple distinct default values. + // void M1([DefaultParameterValue(typeof(C)), DecimalConstantAttribute(0, 0, 0, 0, 0)] decimal o); + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "DecimalConstantAttribute(0, 0, 0, 0, 0)").WithLocation(5, 48) + ); } [WorkItem(529684, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529684")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NamedAndOptionalTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NamedAndOptionalTests.cs index 79b38b427534b..e1f08a7cc9389 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NamedAndOptionalTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NamedAndOptionalTests.cs @@ -932,10 +932,16 @@ public static int Main(){ } "; CreateCompilationWithMscorlib(source, new[] { SystemRef }).VerifyDiagnostics( - // (5,21): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute - Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "Optional"), // (9,21): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute - Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue")); + // public int Bar([DefaultParameterValue(1)]int i = 2) { + Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "DefaultParameterValue").WithLocation(9, 21), + // (9,54): error CS8017: The parameter has multiple distinct default values. + // public int Bar([DefaultParameterValue(1)]int i = 2) { + Diagnostic(ErrorCode.ERR_ParamDefaultValueDiffersFromAttribute, "2").WithLocation(9, 54), + // (5,21): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute + // public int Foo([Optional]object i = null) { + Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "Optional").WithLocation(5, 21) + ); } [WorkItem(10290, "DevDiv_Projects/Roslyn")] diff --git a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs index 36bc839f422cc..41bd0fdad7a5d 100644 --- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs +++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs @@ -1046,7 +1046,16 @@ internal bool HasDateTimeConstantAttribute(EntityHandle token, out ConstantValue AttributeInfo info = FindLastTargetAttribute(token, AttributeDescription.DateTimeConstantAttribute); if (info.HasValue && TryExtractLongValueFromAttribute(info.Handle, out value)) { - defaultValue = ConstantValue.Create(new DateTime(value)); + // if value is outside this range, DateTime would throw when constructed + if (value < DateTime.MinValue.Ticks || value > DateTime.MaxValue.Ticks) + { + defaultValue = ConstantValue.Bad; + } + else + { + defaultValue = ConstantValue.Create(new DateTime(value)); + } + return true; } @@ -1242,7 +1251,7 @@ internal bool TryExtractStringValueFromAttribute(CustomAttributeHandle handle, o return TryExtractValueFromAttribute(handle, out value, s_attributeStringValueExtractor); } - private bool TryExtractLongValueFromAttribute(CustomAttributeHandle handle, out long value) + internal bool TryExtractLongValueFromAttribute(CustomAttributeHandle handle, out long value) { return TryExtractValueFromAttribute(handle, out value, s_attributeLongValueExtractor); } diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs index 4aa49f21909ce..b48048d719624 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs @@ -183,7 +183,15 @@ internal ConstantValue DecodeDecimalConstantValue() internal ConstantValue DecodeDateTimeConstantValue() { - return ConstantValue.Create(new DateTime(this.CommonConstructorArguments[0].DecodeValue(SpecialType.System_Int64))); + long value = this.CommonConstructorArguments[0].DecodeValue(SpecialType.System_Int64); + + // if value is outside this range, DateTime would throw when constructed + if (value < DateTime.MinValue.Ticks || value > DateTime.MaxValue.Ticks) + { + return ConstantValue.Bad; + } + + return ConstantValue.Create(new DateTime(value)); } #endregion diff --git a/src/Compilers/VisualBasic/Portable/Binding/EarlyWellKnownAttributeBinder.vb b/src/Compilers/VisualBasic/Portable/Binding/EarlyWellKnownAttributeBinder.vb index 30e11fcd5f00a..3c507a00c62b0 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/EarlyWellKnownAttributeBinder.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/EarlyWellKnownAttributeBinder.vb @@ -91,7 +91,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Case _ SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.GlobalName, - SyntaxKind.IdentifierName + SyntaxKind.IdentifierName, + SyntaxKind.PredefinedType ' References to constant type members or constant locals. ' References to members of enumeration types. Return True diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb index 47658544fb6fa..55d5e60bd09b0 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceFieldSymbol.vb @@ -342,34 +342,32 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' If not, report ERR_FieldHasMultipleDistinctConstantValues. ''' Private Sub VerifyConstantValueMatches(attrValue As ConstantValue, ByRef arguments As DecodeWellKnownAttributeArguments(Of AttributeSyntax, VisualBasicAttributeData, AttributeLocation)) - If Not attrValue.IsBad Then - Dim data = arguments.GetOrCreateData(Of CommonFieldWellKnownAttributeData)() - Dim constValue As ConstantValue + Dim data = arguments.GetOrCreateData(Of CommonFieldWellKnownAttributeData)() + Dim constValue As ConstantValue - If Me.IsConst Then - If Me.Type.IsDecimalType() OrElse Me.Type.IsDateTimeType() Then - constValue = Me.GetConstantValue(SymbolsInProgress(Of FieldSymbol).Empty) + If Me.IsConst Then + If Me.Type.IsDecimalType() OrElse Me.Type.IsDateTimeType() Then + constValue = Me.GetConstantValue(SymbolsInProgress(Of FieldSymbol).Empty) - If constValue IsNot Nothing AndAlso Not constValue.IsBad AndAlso constValue <> attrValue Then - arguments.Diagnostics.Add(ERRID.ERR_FieldHasMultipleDistinctConstantValues, arguments.AttributeSyntaxOpt.GetLocation()) - End If - Else + If constValue IsNot Nothing AndAlso Not constValue.IsBad AndAlso constValue <> attrValue Then arguments.Diagnostics.Add(ERRID.ERR_FieldHasMultipleDistinctConstantValues, arguments.AttributeSyntaxOpt.GetLocation()) End If - - If data.ConstValue = CodeAnalysis.ConstantValue.Unset Then - data.ConstValue = attrValue - End If Else - constValue = data.ConstValue + arguments.Diagnostics.Add(ERRID.ERR_FieldHasMultipleDistinctConstantValues, arguments.AttributeSyntaxOpt.GetLocation()) + End If - If constValue <> CodeAnalysis.ConstantValue.Unset Then - If constValue <> attrValue Then - arguments.Diagnostics.Add(ERRID.ERR_FieldHasMultipleDistinctConstantValues, arguments.AttributeSyntaxOpt.GetLocation()) - End If - Else - data.ConstValue = attrValue + If data.ConstValue = CodeAnalysis.ConstantValue.Unset Then + data.ConstValue = attrValue + End If + Else + constValue = data.ConstValue + + If constValue <> CodeAnalysis.ConstantValue.Unset Then + If constValue <> attrValue Then + arguments.Diagnostics.Add(ERRID.ERR_FieldHasMultipleDistinctConstantValues, arguments.AttributeSyntaxOpt.GetLocation()) End If + Else + data.ConstValue = attrValue End If End If End Sub diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceParameterSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceParameterSymbol.vb index d791b02b27dff..1fc66e7d4c711 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceParameterSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceParameterSymbol.vb @@ -332,8 +332,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim data = GetEarlyDecodedWellKnownAttributeData() If data IsNot Nothing Then Dim attrValue = data.DefaultParameterValue - If Not attrValue.IsBad AndAlso - attrValue <> ConstantValue.Unset AndAlso + If attrValue <> ConstantValue.Unset AndAlso value <> attrValue Then Binder.ReportDiagnostic(diagnostics, syntax, ERRID.ERR_ParamDefaultValueDiffersFromAttribute) End If diff --git a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.vb b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.vb index b90a6a7389387..3cb5a05ea1055 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.vb @@ -1,15 +1,16 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports Microsoft.CodeAnalysis.Test.Utilities -Imports Microsoft.CodeAnalysis.VisualBasic.Symbols -Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE -Imports Roslyn.Test.Utilities Imports System.Collections.Immutable Imports System.Reflection Imports System.Reflection.Metadata Imports System.Reflection.Metadata.Ecma335 Imports System.Runtime.InteropServices Imports System.Text +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE +Imports Roslyn.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics Public Class AttributeTests_WellKnownAttributes @@ -433,6 +434,341 @@ End Class CompileAndVerify(source, sourceSymbolValidator:=attributeValidator) End Sub + + + Public Sub DateTimeConstantAttribute() + Dim source = + + p1 As DateTime) + End Sub +End Class +]]> + + + + Dim symValidator As Action(Of ModuleSymbol) = + Sub(peModule) + + Dim bar = peModule.GlobalNamespace.GetMember(Of NamedTypeSymbol)("Bar") + Dim method = bar.GetMember(Of MethodSymbol)("Method") + Dim parameters = method.Parameters + Dim theParameter = DirectCast(parameters(0), PEParameterSymbol) + Dim peModuleSymbol = DirectCast(peModule, PEModuleSymbol) + + Assert.Equal(ParameterAttributes.None, theParameter.ParamFlags) + + ' let's find the attribute in the PE metadata + Dim attributeInfo = CodeAnalysis.PEModule.FindTargetAttribute(peModuleSymbol.Module.MetadataReader, theParameter.Handle, AttributeDescription.DateTimeConstantAttribute) + Assert.True(attributeInfo.HasValue) + + Dim attributeValue As Long + Assert.True(peModuleSymbol.Module.TryExtractLongValueFromAttribute(attributeInfo.Handle, attributeValue)) + Assert.Equal(-1L, attributeValue) + + ' check .param has no value + Dim constantHandle = peModuleSymbol.Module.MetadataReader.GetParameter(theParameter.Handle).GetDefaultValue() + Assert.True(constantHandle.IsNil) + End Sub + + CompileAndVerify(source, symbolValidator:=symValidator) + End Sub + + + + Public Sub DateTimeConstantAttributeWithBadDefaultValue() + Dim source = + + Optional p1 As DateTime = # 8/23/1970 3:45:39AM #) As DateTime + Return p1 + End Function + Public Shared Sub Main() + Console.WriteLine(New Bar().Method().Ticks) + End Sub +End Class +]]> + + + + ' The native VB compiler emits this: + ' .method public instance void Method([opt] valuetype [mscorlib]System.DateTime p1) cil managed + ' { + ' .param [1] + ' .custom instance void [mscorlib]System.Runtime.CompilerServices.DateTimeConstantAttribute:: .ctor(Int64) = (1 00 80 73 3E 42 F6 37 A0 08 00 00 ) + ' .custom instance void [mscorlib]System.Runtime.CompilerServices.DateTimeConstantAttribute:: .ctor(Int64) = (1 00 FF FF FF FF FF FF FF FF 00 00 ) + + ' Using the native compiler, the code would output 621558279390000000 + + Dim comp = CreateCompilationWithMscorlib(source) + AssertTheseDiagnostics(comp, + Optional p1 As DateTime = # 8/23/1970 3:45:39AM #) As DateTime + ~~~~~~~~~~~~~~~~~~~~~~~ +]]>) + End Sub + + + + Public Sub DateTimeConstantAttributeWithValidDefaultValue() + Dim source = + + Optional p1 As DateTime = # 8/23/1970 3:45:39AM #) As DateTime + Return p1 + End Function + Public Shared Sub Main() + Console.WriteLine(New Bar().Method().Ticks) + End Sub +End Class +]]> + + + + ' The native VB compiler emits this: + ' .method public instance valuetype [mscorlib]System.DateTime + ' Method([opt] valuetype [mscorlib]System.DateTime p1) cil managed + ' { + ' .param [1] + ' .custom instance void [mscorlib]System.Runtime.CompilerServices.DateTimeConstantAttribute:: .ctor(Int64) = (1 00 2A 00 00 00 00 00 00 00 00 00 ) + ' .custom instance void [mscorlib]System.Runtime.CompilerServices.DateTimeConstantAttribute:: .ctor(Int64) = (1 00 80 73 3E 42 F6 37 A0 08 00 00 ) + + ' Using the native compiler, the code would output 621558279390000000 + + Dim comp = CreateCompilationWithMscorlib(source) + AssertTheseDiagnostics(comp, + Optional p1 As DateTime = # 8/23/1970 3:45:39AM #) As DateTime + ~~~~~~~~~~~~~~~~~~~~~~~ +]]>) + End Sub + + + + Public Sub DateTimeConstantAttributeWithBadDefaultValueOnField() + Dim source = + + + Public Const F As DateTime = # 8/23/1970 3:45:39AM # + + Public Shared Sub Main() + Console.WriteLine(Bar.F.Ticks) + End Sub +End Class +]]> + + + + ' The native compiler would output 621558279390000000 + Dim comp = CreateCompilationWithMscorlib(source) + comp.AssertTheseDiagnostics( + ~~~~~~~~~~~~~~~~~~~~ +]]>) + + End Sub + + + + Public Sub DateTimeConstantAttributeWithValidDefaultValueOnField() + Dim source = + + + Public Const F As DateTime = # 8/23/1970 3:45:39AM # + + Public Shared Sub Main() + Console.WriteLine(Bar.F.Ticks) + End Sub +End Class +]]> + + + + ' With the native VB compiler, this code outputs 621558279390000000 + Dim comp = CreateCompilationWithMscorlib(source) + comp.AssertTheseDiagnostics( + ~~~~~~~~~~~~~~~~~~~~ +]]>) + + End Sub + + + + Public Sub DateTimeConstantAttributeReferencedViaRef() + Dim source1 = + + p1 As DateTime) + End Sub +End Class +]]> + + + + Dim source2 = + + + + + + Dim libComp = CreateCompilationWithMscorlib(source1) + Dim libCompRef = New VisualBasicCompilationReference(libComp) + + Dim comp2 = CreateCompilationWithMscorlib(source2, references:={libCompRef}) + AssertTheseDiagnostics(comp2, + ) + + Dim libAssemblyRef = libComp.EmitToImageReference() + Dim comp3 = CreateCompilationWithMscorlib(source2, references:={libAssemblyRef}) + AssertTheseDiagnostics(comp3, + ) + End Sub + + + + Public Sub LoadingDateTimeConstantWithBadValueOnField() + Dim ilSource = + + Dim source = + + + + + + ' Using the native compiler, this code crashed + Dim ilReference = CompileIL(ilSource.Value) + Dim comp = CreateCompilationWithMscorlib(source, references:={ilReference}) + AssertTheseDiagnostics(comp, + ) + End Sub + + + + Public Sub LoadingDateTimeConstantWithBadValue() + Dim ilSource = + + Dim source = + + + + + + Dim ilReference = CompileIL(ilSource.Value) + CompileAndVerify(source, expectedOutput:="0", additionalRefs:={ilReference}) + ' The native compiler would produce a working exe, but that exe would fail at runtime + End Sub + Public Sub TestDecimalConstantAttribute() diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/OptionalArgumentsTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/OptionalArgumentsTests.vb index c18a653d3a32f..0e646e3a974ed 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/OptionalArgumentsTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/OptionalArgumentsTests.vb @@ -1152,6 +1152,7 @@ Imports System.Runtime.InteropServices Interface I Sub M1( o As Decimal) Sub M2( o As Decimal) + Sub M3( o As Decimal) End Interface ]]> @@ -1160,9 +1161,15 @@ End Interface BC30002: Type 'C' is not defined. Sub M1( o As Decimal) ~ +BC37226: The parameter has multiple distinct default values. + Sub M1( o As Decimal) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BC30002: Type 'C' is not defined. Sub M2( o As Decimal) ~ +BC37226: The parameter has multiple distinct default values. + Sub M3( o As Decimal) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ]]>) End Sub