From ff2425663dab1ddd417d56374b59abc6b5874d24 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Fri, 10 Mar 2023 09:57:41 -0800 Subject: [PATCH] Warn when taking address of local or parameter in async method (#66915) --- .../Portable/Binder/Binder.ValueChecks.cs | 9 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../CSharp/Portable/Errors/ErrorFacts.cs | 5 + .../Generated/ErrorFacts.Generated.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 10 + .../Portable/xlf/CSharpResources.de.xlf | 10 + .../Portable/xlf/CSharpResources.es.xlf | 10 + .../Portable/xlf/CSharpResources.fr.xlf | 10 + .../Portable/xlf/CSharpResources.it.xlf | 10 + .../Portable/xlf/CSharpResources.ja.xlf | 10 + .../Portable/xlf/CSharpResources.ko.xlf | 10 + .../Portable/xlf/CSharpResources.pl.xlf | 10 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 + .../Portable/xlf/CSharpResources.ru.xlf | 10 + .../Portable/xlf/CSharpResources.tr.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 + .../Test/Emit/CodeGen/CodeGenAsyncTests.cs | 201 ++++++++++++++++++ .../Test/Syntax/Diagnostics/DiagnosticTest.cs | 4 + 20 files changed, 357 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 11d6655d7e872..97e870a509fa8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -817,6 +817,11 @@ private static bool CheckNotNamespaceOrType(BoundExpression expr, BindingDiagnos private bool CheckLocalValueKind(SyntaxNode node, BoundLocal local, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) { + if (valueKind == BindValueKind.AddressOf && this.IsInAsyncMethod()) + { + Error(diagnostics, ErrorCode.WRN_AddressOfInAsync, node); + } + // Local constants are never variables. Local variables are sometimes // not to be treated as variables, if they are fixed, declared in a using, // or declared in a foreach. @@ -906,6 +911,10 @@ internal partial class Binder private bool CheckParameterValueKind(SyntaxNode node, BoundParameter parameter, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) { Debug.Assert(!RequiresAssignableVariable(BindValueKind.AddressOf)); + if (valueKind == BindValueKind.AddressOf && this.IsInAsyncMethod()) + { + Error(diagnostics, ErrorCode.WRN_AddressOfInAsync, node); + } ParameterSymbol parameterSymbol = parameter.ParameterSymbol; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index b6e4c1b761ffb..b60ab0cfc078e 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7493,4 +7493,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Unexpected parameter list. + + The '&' operator should not be used on parameters or local variables in async methods. + + + The '&' operator should not be used on parameters or local variables in async methods. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 5b7035be3ca34..1f079437c4642 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2181,11 +2181,11 @@ internal enum ErrorCode ERR_RefReturnPrimaryConstructorParameter = 9120, ERR_StructLayoutCyclePrimaryConstructorParameter = 9121, ERR_UnexpectedParameterList = 9122, + WRN_AddressOfInAsync = 9123, ERR_BadRefInUsingAlias = 9130, ERR_BadUnsafeInUsingDirective = 9131, ERR_BadNullableReferenceTypeInUsingAlias = 9132, - #endregion // Note: you will need to do the following after adding warnings: diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index ff49ac26476f7..fb37825ffa6c3 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -206,6 +206,10 @@ internal static int GetWarningLevel(ErrorCode code) // docs/compilers/CSharp/Warnversion Warning Waves.md switch (code) { + case ErrorCode.WRN_AddressOfInAsync: + // Warning level 8 is exclusively for warnings introduced in the compiler + // shipped with dotnet 8 (C# 12) and that can be reported for pre-existing code. + return 8; case ErrorCode.WRN_LowerCaseTypeName: // Warning level 7 is exclusively for warnings introduced in the compiler // shipped with dotnet 7 (C# 11) and that can be reported for pre-existing code. @@ -2299,6 +2303,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_RefReturnPrimaryConstructorParameter: case ErrorCode.ERR_StructLayoutCyclePrimaryConstructorParameter: case ErrorCode.ERR_UnexpectedParameterList: + case ErrorCode.WRN_AddressOfInAsync: case ErrorCode.ERR_BadRefInUsingAlias: case ErrorCode.ERR_BadUnsafeInUsingDirective: case ErrorCode.ERR_BadNullableReferenceTypeInUsingAlias: diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index 29b9c3ca54a3b..c1203a2275310 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -314,6 +314,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_ParamsArrayInLambdaOnly: case ErrorCode.WRN_CapturedPrimaryConstructorParameterPassedToBase: case ErrorCode.WRN_UnreadPrimaryConstructorParameter: + case ErrorCode.WRN_AddressOfInAsync: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 6cbca004767cb..80a11921ae2eb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2012,6 +2012,16 @@ přístup k elementu ukazatele + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Sestavení {0}, které obsahuje typ {1}, se odkazuje na architekturu .NET Framework, což se nepodporuje. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 2807b02f1cb40..906893fa08255 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2012,6 +2012,16 @@ Zeigerelementzugriff + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Die Assembly "{0}" mit dem Typ "{1}" verweist auf das .NET Framework. Dies wird nicht unterstützt. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 75b493d7ee539..dc9dd51f1da34 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2012,6 +2012,16 @@ acceso al elemento de puntero + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. El ensamblado "{0}" que contiene el tipo "{1}" hace referencia a .NET Framework, lo cual no se admite. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index b5116ecf820ff..b89ac60855d18 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2012,6 +2012,16 @@ accès à l’élément de pointeur + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. L'assembly '{0}' contenant le type '{1}' référence le .NET Framework, ce qui n'est pas pris en charge. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 8f9184b51b349..3432fc126d1a8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2012,6 +2012,16 @@ accesso all'elemento puntatore + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. L'assembly '{0}' che contiene il tipo '{1}' fa riferimento a .NET Framework, che non è supportato. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index f16659bf8719b..9c56398a45af8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2012,6 +2012,16 @@ ポインター要素アクセス + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 型 '{1}' を含むアセンブリ '{0}' が .NET Framework を参照しています。これはサポートされていません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 085c91e7fd6b5..c00f566827726 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2012,6 +2012,16 @@ 포인터 요소 액세스 + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. '{1}' 형식을 포함하는 '{0}' 어셈블리가 지원되지 않는 .NET Framework를 참조합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 09f600f7686a6..edb7c113e1670 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2012,6 +2012,16 @@ dostęp do elementu wskaźnika + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Zestaw „{0}” zawierający typ „{1}” odwołuje się do platformy .NET Framework, co nie jest obsługiwane. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index d22c30ee12d4c..b3b22523740c1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2012,6 +2012,16 @@ acesso ao elemento de ponteiro + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. O assembly '{0}' contendo o tipo '{1}' referencia o .NET Framework, mas não há suporte para isso. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 4259d48bc6da4..670e88fa2dc15 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2012,6 +2012,16 @@ доступ к элементу указателя + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. Сборка "{0}", содержащая тип "{1}", ссылается на платформу .NET Framework, которая не поддерживается. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a196995c96061..0dc7d4212f72a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2012,6 +2012,16 @@ işaretçi öğesi erişimi + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. '{1}' türünü içeren '{0}' bütünleştirilmiş kodu, desteklenmeyen .NET Framework'e başvuruyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index bdf1b445af928..c21aed7ba1fdc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2012,6 +2012,16 @@ 指针元素访问 + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 包含类型“{1}”的程序集“{0}”引用了 .NET Framework,而此操作不受支持。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 14c162e06beaf..0251784d7ac06 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2012,6 +2012,16 @@ 指標元素存取 + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + + + The '&' operator should not be used on parameters or local variables in async methods. + The '&' operator should not be used on parameters or local variables in async methods. + + The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. 包含類型 '{1}' 的組件 '{0}' 參考了 .NET Framework,此情形不受支援。 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 256a65e728b8a..4e88eea1564b5 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -871,6 +871,207 @@ public static void Main() CompileAndVerify(source, expectedOutput: "0", options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66829")] + public void AddressOf_WithinAwaitBoundary() + { + var source = """ + using System; + using System.Threading.Tasks; + + class Program + { + public static async Task Main() + { + long x = 1; + + unsafe + { + Console.Write(*&x); + } + + unsafe + { + Console.Write(*&x); + } + + await Task.Delay(1000); + } + } + """; + + var diagnostics = new[] + { + // (12,29): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // Console.Write(*&x); + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(12, 29), + // (17,29): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // Console.Write(*&x); + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(17, 29) + }; + + CompileAndVerify(source, options: TestOptions.UnsafeDebugExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: "11", symbolValidator: debugSymbolValidator, verify: Verification.Fails) + .VerifyDiagnostics(diagnostics); + CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: "11", symbolValidator: releaseSymbolValidator, verify: Verification.Fails) + .VerifyDiagnostics(diagnostics); + + void debugSymbolValidator(ModuleSymbol module) + { + var stateMachine = module.GlobalNamespace.GetMember("Program.
d__0"); + var hoistedField = stateMachine.GetMember("5__1"); + Assert.Equal(SpecialType.System_Int64, hoistedField.Type.SpecialType); + } + + void releaseSymbolValidator(ModuleSymbol module) + { + var stateMachine = module.GlobalNamespace.GetMember("Program.
d__0"); + // Test that there is no state-machine field based on 'x'. + Assert.Empty(stateMachine.GetMembers().Where(m => m.Name.StartsWith(""))); + } + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66829")] + public void AddressOf_AcrossAwaitBoundary() + { + var source = """ + using System; + using System.Threading.Tasks; + + class Program + { + public static async Task Main() + { + long x = 1; + + unsafe + { + Console.Write(*&x); + } + + await Task.Delay(1000); + + unsafe + { + Console.Write(*&x); + } + } + } + """; + + var diagnostics = new[] + { + // (12,29): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // Console.Write(*&x); + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(12, 29), + // (19,29): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // Console.Write(*&x); + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(19, 29) + }; + + CompileAndVerify(source, options: TestOptions.UnsafeDebugExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: "11", symbolValidator: debugSymbolValidator, verify: Verification.Fails) + .VerifyDiagnostics(diagnostics); + CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: "10", symbolValidator: releaseSymbolValidator, verify: Verification.Fails) + .VerifyDiagnostics(diagnostics); + + void debugSymbolValidator(ModuleSymbol module) + { + var stateMachine = module.GlobalNamespace.GetMember("Program.
d__0"); + var hoistedField = stateMachine.GetMember("5__1"); + Assert.Equal(SpecialType.System_Int64, hoistedField.Type.SpecialType); + } + + void releaseSymbolValidator(ModuleSymbol module) + { + var stateMachine = module.GlobalNamespace.GetMember("Program.
d__0"); + // Test that there is no state-machine field based on 'x'. + Assert.Empty(stateMachine.GetMembers().Where(m => m.Name.StartsWith(""))); + } + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/66829")] + public void AddressOf_Fixed() + { + var source = """ + using System.Threading.Tasks; + // This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + #pragma warning disable 1998 + class Program + { + int F; + + public static unsafe async Task Main() + { + Program prog = new Program(); + int* ptr = &prog.F; // 1 + fixed (int* ptr1 = &prog.F) { } + + int local = 0; + int* localPtr = &local; // 2 + fixed (int* localPtr1 = &local) { } // 3, 4 + + S structLocal = default; + int* innerPtr = &structLocal.F; // 5 + fixed (int* innerPtr1 = &structLocal.F) { } // 6, 7 + + localFunc(); + void localFunc() + { + int localFuncLocal = 0; + int* localFuncLocalPtr = &localFuncLocal; + } + + _ = asyncLocalFunc(); + async Task asyncLocalFunc() + { + int localFuncLocal = 0; + int* localFuncLocalPtr = &localFuncLocal; // 8 + } + } + } + + struct S { public int F; } + """; + + CreateCompilation(source, options: TestOptions.UnsafeDebugExe).VerifyDiagnostics( + // (11,20): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer + // int* ptr = &prog.F; // 1 + Diagnostic(ErrorCode.ERR_FixedNeeded, "&prog.F").WithLocation(11, 20), + // (15,26): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int* localPtr = &local; // 2 + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "local").WithLocation(15, 26), + // (16,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + // fixed (int* localPtr1 = &local) { } // 3, 4 + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&local").WithLocation(16, 33), + // (16,34): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // fixed (int* localPtr1 = &local) { } // 3, 4 + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "local").WithLocation(16, 34), + // (19,26): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int* innerPtr = &structLocal.F; // 5 + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "structLocal.F").WithLocation(19, 26), + // (20,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + // fixed (int* innerPtr1 = &structLocal.F) { } // 6, 7 + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&structLocal.F").WithLocation(20, 33), + // (20,34): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // fixed (int* innerPtr1 = &structLocal.F) { } // 6, 7 + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "structLocal.F").WithLocation(20, 34), + // (33,39): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int* localFuncLocalPtr = &localFuncLocal; // 8 + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "localFuncLocal").WithLocation(33, 39)); + + CreateCompilation(source, options: TestOptions.UnsafeDebugExe.WithWarningLevel(7)).VerifyDiagnostics( + // (11,20): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer + // int* ptr = &prog.F; // 1 + Diagnostic(ErrorCode.ERR_FixedNeeded, "&prog.F").WithLocation(11, 20), + // (16,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + // fixed (int* localPtr1 = &local) { } // 3, 4 + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&local").WithLocation(16, 33), + // (20,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + // fixed (int* innerPtr1 = &structLocal.F) { } // 6, 7 + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&structLocal.F").WithLocation(20, 33)); + } + [Fact] public void Inference() { diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 13dc64980c76f..3eddc361b3951 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -440,6 +440,10 @@ public void WarningLevel_2() // These are the warnings introduced with the warning "wave" shipped with dotnet 7 and C# 11. Assert.Equal(7, ErrorFacts.GetWarningLevel(errorCode)); break; + case ErrorCode.WRN_AddressOfInAsync: + // These are the warnings introduced with the warning "wave" shipped with dotnet 8 and C# 12. + Assert.Equal(8, ErrorFacts.GetWarningLevel(errorCode)); + break; default: // If a new warning is added, this test will fail // and whoever is adding the new warning will have to update it with the expected error level.