diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpUseVolatileReadWriteFixer.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpUseVolatileReadWriteFixer.cs new file mode 100644 index 0000000000..086401c930 --- /dev/null +++ b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpUseVolatileReadWriteFixer.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.NetCore.Analyzers.Usage; + +namespace Microsoft.NetCore.CSharp.Analyzers.Usage +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal sealed class CSharpUseVolatileReadWriteFixer : UseVolatileReadWriteFixer + { + protected override SyntaxNode GetArgumentForVolatileReadCall(IArgumentOperation argument, IParameterSymbol volatileReadParameter) + { + var argumentSyntax = (ArgumentSyntax)argument.Syntax; + if (argumentSyntax.NameColon is null) + { + return argumentSyntax; + } + + return argumentSyntax.WithNameColon(SyntaxFactory.NameColon(volatileReadParameter.Name)); + } + + protected override IEnumerable GetArgumentForVolatileWriteCall(ImmutableArray arguments, ImmutableArray volatileWriteParameters) + { + foreach (var argument in arguments) + { + var argumentSyntax = (ArgumentSyntax)argument.Syntax; + if (argumentSyntax.NameColon is null) + { + yield return argumentSyntax; + } + else + { + var parameterName = volatileWriteParameters[argument.Parameter!.Ordinal].Name; + yield return argumentSyntax.WithNameColon(SyntaxFactory.NameColon(parameterName)); + } + } + } + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 237745047a..aa3ec90377 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -2147,4 +2147,7 @@ Widening and user defined conversions are not supported with generic types. Use 'IsEmpty' + + Replace obsolete call + diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/UseVolatileReadWrite.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/UseVolatileReadWrite.Fixer.cs new file mode 100644 index 0000000000..3b051f2058 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/UseVolatileReadWrite.Fixer.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.NetCore.Analyzers.Usage +{ + public abstract class UseVolatileReadWriteFixer : CodeFixProvider + { + private const string ThreadVolatileReadMethodName = nameof(Thread.VolatileRead); + private const string ThreadVolatileWriteMethodName = nameof(Thread.VolatileWrite); + private const string VolatileReadMethodName = nameof(Volatile.Read); + private const string VolatileWriteMethodName = nameof(Volatile.Write); + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var node = root.FindNode(context.Span, getInnermostNodeForTie: true); + var semanticModel = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + var typeProvider = WellKnownTypeProvider.GetOrCreate(semanticModel.Compilation); + var operation = semanticModel.GetOperation(node); + if (typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingThread) is not INamedTypeSymbol threadType + || typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingVolatile) is not INamedTypeSymbol volatileType + || operation is not IInvocationOperation invocationOperation) + { + return; + } + + var obsoleteMethodsBuilder = ImmutableArray.CreateBuilder(); + obsoleteMethodsBuilder.AddRange(threadType.GetMembers(ThreadVolatileReadMethodName).OfType()); + obsoleteMethodsBuilder.AddRange(threadType.GetMembers(ThreadVolatileWriteMethodName).OfType()); + var obsoleteMethods = obsoleteMethodsBuilder.ToImmutable(); + + var volatileReadMethod = volatileType.GetMembers(VolatileReadMethodName).OfType().FirstOrDefault(); + var volatileWriteMethod = volatileType.GetMembers(VolatileWriteMethodName).OfType().FirstOrDefault(); + + if (!SymbolEqualityComparer.Default.Equals(invocationOperation.TargetMethod.ContainingType, threadType) + || !obsoleteMethods.Any(SymbolEqualityComparer.Default.Equals, invocationOperation.TargetMethod) + || volatileReadMethod is null + || volatileWriteMethod is null) + { + return; + } + + var codeAction = CodeAction.Create( + MicrosoftNetCoreAnalyzersResources.DoNotUseThreadVolatileReadWriteCodeFixTitle, + ReplaceObsoleteCall, + equivalenceKey: nameof(MicrosoftNetCoreAnalyzersResources.DoNotUseThreadVolatileReadWriteCodeFixTitle)); + + context.RegisterCodeFix(codeAction, context.Diagnostics); + + return; + + async Task ReplaceObsoleteCall(CancellationToken cancellationToken) + { + var editor = await DocumentEditor.CreateAsync(context.Document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + + string methodName; + IEnumerable arguments; + if (invocationOperation.TargetMethod.Name.Equals(ThreadVolatileReadMethodName, StringComparison.Ordinal)) + { + methodName = VolatileReadMethodName; + arguments = [GetArgumentForVolatileReadCall(invocationOperation.Arguments[0], volatileReadMethod.Parameters[0])]; + } + else + { + methodName = VolatileWriteMethodName; + arguments = GetArgumentForVolatileWriteCall(invocationOperation.Arguments, volatileWriteMethod.Parameters); + } + + var methodExpression = generator.MemberAccessExpression( + generator.TypeExpressionForStaticMemberAccess(volatileType), + methodName); + var methodInvocation = generator.InvocationExpression(methodExpression, arguments); + + editor.ReplaceNode(invocationOperation.Syntax, methodInvocation.WithTriviaFrom(invocationOperation.Syntax)); + + return context.Document.WithSyntaxRoot(editor.GetChangedRoot()); + } + } + + protected abstract SyntaxNode GetArgumentForVolatileReadCall(IArgumentOperation argument, IParameterSymbol volatileReadParameter); + + protected abstract IEnumerable GetArgumentForVolatileWriteCall(ImmutableArray arguments, ImmutableArray volatileWriteParameters); + + public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create("SYSLIB0054"); + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf index 98fbd1b072..1be99c1d36 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -1163,6 +1163,11 @@ Obecné přetypování (IL unbox.any) používané sekvencí vrácenou metodou E Nepoužívat stackalloc ve smyčkách + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Častější pravidelná aktivita bude zatěžovat procesor a ovlivňovat časovače neaktivity, které šetří energii a vypínají displej a pevné disky. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf index c9595168fd..22dee2f2e6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -1163,6 +1163,11 @@ Erweiterungen und benutzerdefinierte Konvertierungen werden bei generischen Type stackalloc nicht in Schleifen verwenden + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Regelmäßige Aktivitäten mit einer höheren Frequenz belasten die CPU und beeinflussen energiesparende Leerlauftimer, mit denen die Anzeige sowie die Festplatten ausgeschaltet werden. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf index c75a519d3b..e9791484aa 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -1163,6 +1163,11 @@ La ampliación y las conversiones definidas por el usuario no se admiten con tip No utilizar stackalloc en los bucles + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Una actividad periódica más frecuente ocupará la CPU e interferirá con los temporizadores de inactividad para ahorro de energía que apagan el monitor y los discos duros. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf index 0d6fa88ded..f43c7f1e45 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -1163,6 +1163,11 @@ Les conversions étendues et définies par l’utilisateur ne sont pas prises en N'utilisez pas stackalloc dans les boucles + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Une activité régulière à plus grande fréquence occupe le processeur et interfère avec les minuteurs d'inactivité qui déclenchent la mise en veille de l'écran et des disques durs pour économiser de l'énergie. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf index ae2eef68d0..404f6d8bef 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -1163,6 +1163,11 @@ L'ampliamento e le conversioni definite dall'utente non sono supportate con tipi Non usare stackalloc nei cicli + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Un'attività periodica più frequente tiene la CPU occupata e interferisce con i timer di inattività per il risparmio di energia che disattivano lo schermo e i dischi rigidi. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf index 2da9b89e8a..8c5ce5e334 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -1163,6 +1163,11 @@ Enumerable.OfType<T> で使用されるジェネリック型チェック ( stackalloc はループ内で使用不可 + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. 頻度の高い定期的な動作は CPU のビジー状態を維持し、画面およびハード ディスクの電源を切る節電アイドル タイマーに影響します。 diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf index 0e6e6acfde..46b5f4069e 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -1163,6 +1163,11 @@ Enumerable.OfType<T>에서 사용하는 제네릭 형식 검사(C# 'is' 루프에서 stackalloc을 사용하면 안 됨 + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. 정기적인 작업의 실행 빈도가 높아지면 CPU 사용률도 높아져 디스플레이 및 하드 디스크를 끄는 절전 유휴 타이머에 방해가 됩니다. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf index 37b4551569..0b50729af9 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -1163,6 +1163,11 @@ Konwersje poszerzane i zdefiniowane przez użytkownika nie są obsługiwane w pr Nie używaj słowa kluczowego stackalloc w pętlach + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Działania okresowe wykonywane z dużą częstotliwością utrzymują zajętość procesora CPU i wpływają na czasomierze bezczynności funkcji oszczędzania energii, które powodują wyłączanie ekranu i dysków twardych. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf index 334c4c01e4..f93551ea42 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf @@ -1163,6 +1163,11 @@ Ampliação e conversões definidas pelo usuário não são compatíveis com tip Não use stackalloc em loops + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Atividade periódica de alta frequência manterá a CPU ocupada e interferirá nos medidores de tempo ocioso para economia de energia que desligam o visor e os discos rígidos. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf index 236926205b..060375fe0f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -1163,6 +1163,11 @@ Widening and user defined conversions are not supported with generic types.Запрет использования stackalloc в циклах + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Периодическая активность с более высокой частотой заставит ЦП переключаться в активный режим и помешает работе энергосберегающих таймеров простоя, которые отключают дисплей и жесткие диски. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf index 2aa5d46590..d0fbb40ea5 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -1163,6 +1163,11 @@ Genel türlerde genişletme ve kullanıcı tanımlı dönüştürmeler desteklen Döngüler içinde stackalloc kullanmayın + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. Daha yüksek frekanslı düzenli etkinlik, CPU’nun meşgul kalmasına neden olmasının yanı sıra ekranı ve sabit diskleri kapatarak güç tasarrufu sağlayan boşta süreölçerlerini etkileyebilir. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf index d0c5991d99..99bf8d44f6 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf @@ -1163,6 +1163,11 @@ Enumerable.OfType<T> 使用的泛型类型检查 (C# 'is' operator/IL 'isi 不要循环使用 stackalloc + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. 频率较高的定期活动会使 CPU 处于忙状态并且干扰具有节能功能(关闭显示器和硬盘)的空闲计时器。 diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf index 3243a4236d..ad57a6fe7c 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf @@ -1163,6 +1163,11 @@ Enumerable.OfType<T> 使用的一般型別檢查 (C# 'is' operator/IL 'isi 請勿在迴圈中使用 stackalloc + + Replace obsolete call + Replace obsolete call + + Higher-frequency periodic activity will keep the CPU busy and interfere with power-saving idle timers that turn off the display and hard disks. 更高頻率的週期性活動,會讓 CPU 一直處於忙碌狀態,且會干擾關閉顯示器與硬碟的省電閒置計時器。 diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/UseVolatileReadWriteTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/UseVolatileReadWriteTests.cs new file mode 100644 index 0000000000..366b746cc5 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/UseVolatileReadWriteTests.cs @@ -0,0 +1,909 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer, + Microsoft.NetCore.CSharp.Analyzers.Usage.CSharpUseVolatileReadWriteFixer>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer, + Microsoft.NetCore.VisualBasic.Analyzers.Usage.BasicUseVolatileReadWriteFixer>; + +namespace Microsoft.NetCore.Analyzers.Usage.UnitTests +{ + public sealed class UseVolatileReadWriteTests + { + private const string CsharpSystemThreadingThread = """ + namespace System.Threading + { + public sealed class Thread + { + private const string ObsoletionDiagnosticId = "SYSLIB0054"; + + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static byte VolatileRead(ref byte address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static double VolatileRead(ref double address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static short VolatileRead(ref short address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static int VolatileRead(ref int address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static long VolatileRead(ref long address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static IntPtr VolatileRead(ref IntPtr address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static object VolatileRead(ref object address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static sbyte VolatileRead(ref sbyte address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static float VolatileRead(ref float address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static ushort VolatileRead(ref ushort address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static uint VolatileRead(ref uint address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static ulong VolatileRead(ref ulong address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static UIntPtr VolatileRead(ref UIntPtr address) => Volatile.Read(ref address); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref byte address, byte value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref double address, double value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref short address, short value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref int address, int value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref long address, long value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref IntPtr address, IntPtr value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref object address, object value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref sbyte address, sbyte value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref float address, float value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref ushort address, ushort value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref uint address, uint value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref ulong address, ulong value) => Volatile.Write(ref address, value); + [Obsolete(DiagnosticId = ObsoletionDiagnosticId)] + public static void VolatileWrite(ref UIntPtr address, UIntPtr value) => Volatile.Write(ref address, value); + } + } + """; + + private const string VisualBasicSystemThreadingThread = """ + Namespace System.Threading + Public NotInheritable Class Thread + Private Const ObsoletionDiagnosticId = "SYSLIB0054" + + + Public Shared Function VolatileRead(ByRef address As Byte) As Byte + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As Double) As Double + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As Short) As Short + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As Int32) As Int32 + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As Int64) As Int64 + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As IntPtr) As IntPtr + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As Object) As Object + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As SByte) As SByte + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As Single) As Single + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As UInt16) As UInt16 + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As UInt32) As UInt32 + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As UInt64) As UInt64 + Volatile.Read(address) + End Function + + Public Shared Function VolatileRead(ByRef address As UIntPtr) As UIntPtr + Volatile.Read(address) + End Function + + Public Shared Sub VolatileWrite(ByRef address As Byte, value As Byte) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As Double, value As Double) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As Int16, value As Int16) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As Int32, value As Int32) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As Int64, value As Int64) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As IntPtr, value As IntPtr) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As Object, value As Object) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As SByte, value As SByte) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As Single, value As Single) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As UInt16, value As UInt16) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As UInt32, value As UInt32) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As Uint64, value As Uint64) + Volatile.Write(address, value) + End Sub + + Public Shared Sub VolatileWrite(ByRef address As UIntPtr, value As UIntPtr) + Volatile.Write(address, value) + End Sub + End Class + End Namespace + """; + + public static readonly TheoryData CSharpTypes = new() + { + "IntPtr", + "UIntPtr", + "byte", + "double", + "float", + "int", + "long", + "sbyte", + "short", + "uint", + "ulong", + "ushort" + }; + + public static readonly TheoryData VisualBasicTypes = new() + { + "IntPtr", + "UIntPtr", + "Byte", + "Double", + "Single", + "Integer", + "Long", + "Object", + "Sbyte", + "Short", + "UInteger", + "ULong", + "UShort" + }; + + [Theory] + [MemberData(nameof(CSharpTypes))] + public Task CS_UseVolatileRead(string type) + { + var code = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg) + { + {|#0:Thread.VolatileRead(ref arg)|}; + } + } + """; + var fixedCode = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg) + { + Volatile.Read(ref arg); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(CSharpTypes))] + public Task CS_UseVolatileRead_WithNamedArguments(string type) + { + var code = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg) + { + {|#0:Thread.VolatileRead(address: ref arg)|}; + } + } + """; + var fixedCode = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg) + { + Volatile.Read(location: ref arg); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(CSharpTypes))] + public Task CS_UseVolatileRead_WithTrivia(string type) + { + var code = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg) + { + // Trivia prefix + {|#0:Thread.VolatileRead(ref arg)|}; // Trivia infix + // Trivia suffix + } + } + """; + var fixedCode = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg) + { + // Trivia prefix + Volatile.Read(ref arg); // Trivia infix + // Trivia suffix + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Fact] + public Task CS_UseVolatileRead_Nullable() + { + const string code = """ + using System; + using System.Threading; + + #nullable enable + class Test + { + void M(object? arg) + { + {|#0:Thread.VolatileRead(ref arg)|}; + } + } + """; + const string fixedCode = """ + using System; + using System.Threading; + + #nullable enable + class Test + { + void M(object? arg) + { + Volatile.Read(ref arg); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Fact] + public Task CS_UseVolatileRead_NonNullable() + { + const string code = """ + using System; + using System.Threading; + + class Test + { + void M(object arg) + { + {|#0:Thread.VolatileRead(ref arg)|}; + } + } + """; + const string fixedCode = """ + using System; + using System.Threading; + + class Test + { + void M(object arg) + { + Volatile.Read(ref arg); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(CSharpTypes))] + public Task CS_UseVolatileWrite(string type) + { + var code = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + {|#0:Thread.VolatileWrite(ref arg, value)|}; + } + } + """; + var fixedCode = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + Volatile.Write(ref arg, value); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(CSharpTypes))] + public Task CS_UseVolatileWrite_WithNamedArguments(string type) + { + var code = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + {|#0:Thread.VolatileWrite(address: ref arg, value: value)|}; + } + } + """; + var fixedCode = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + Volatile.Write(location: ref arg, value: value); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(CSharpTypes))] + public Task CS_UseVolatileWrite_WithReversedArguments(string type) + { + var code = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + {|#0:Thread.VolatileWrite(value: value, address: ref arg)|}; + } + } + """; + var fixedCode = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + Volatile.Write(value: value, location: ref arg); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(CSharpTypes))] + public Task CS_UseVolatileWrite_WithSingleNamedArgument(string type) + { + var code = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + {|#0:Thread.VolatileWrite(address: ref arg, value)|}; + } + } + """; + var fixedCode = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + Volatile.Write(location: ref arg, value); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(CSharpTypes))] + public Task CS_UseVolatileWrite_WithTrivia(string type) + { + var code = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + // Trivia prefix + {|#0:Thread.VolatileWrite(ref arg, value)|}; // Trivia infix + // Trivia suffix + } + } + """; + var fixedCode = $$""" + using System; + using System.Threading; + + #nullable enable + class Test + { + void M({{type}} arg, {{type}} value) + { + // Trivia prefix + Volatile.Write(ref arg, value); // Trivia infix + // Trivia suffix + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Fact] + public Task CS_UseVolatileWrite_Nullable() + { + const string code = """ + using System; + using System.Threading; + + #nullable enable + class Test + { + void M(object? arg, object? value) + { + {|#0:Thread.VolatileWrite(ref arg, value)|}; + } + } + """; + const string fixedCode = """ + using System; + using System.Threading; + + #nullable enable + class Test + { + void M(object? arg, object? value) + { + Volatile.Write(ref arg, value); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Fact] + public Task CS_UseVolatileWrite_NonNullable() + { + const string code = """ + using System; + using System.Threading; + + class Test + { + void M(object arg, object value) + { + {|#0:Thread.VolatileWrite(ref arg, value)|}; + } + } + """; + const string fixedCode = """ + using System; + using System.Threading; + + class Test + { + void M(object arg, object value) + { + Volatile.Write(ref arg, value); + } + } + """; + + return VerifyCsharpAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(VisualBasicTypes))] + public Task VB_UseVolatileRead(string type) + { + var code = $$""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {{type}}) + {|#0:Thread.VolatileRead(arg)|} + End Sub + End Class + """; + var fixedCode = $""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {type}) + Volatile.Read(arg) + End Sub + End Class + """; + + return VerifyVisualBasicAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(VisualBasicTypes))] + public Task VB_UseVolatileRead_WithNamedArguments(string type) + { + var code = $$""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {{type}}) + {|#0:Thread.VolatileRead(address:=arg)|} + End Sub + End Class + """; + var fixedCode = $""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {type}) + Volatile.Read(location:=arg) + End Sub + End Class + """; + + return VerifyVisualBasicAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(VisualBasicTypes))] + public Task VB_UseVolatileRead_WithTrivia(string type) + { + var code = $$""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {{type}}) + ' Trivia prefix + {|#0:Thread.VolatileRead(arg)|} ' Trivia infix + ' Trivia suffix + End Sub + End Class + """; + var fixedCode = $""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {type}) + ' Trivia prefix + Volatile.Read(arg) ' Trivia infix + ' Trivia suffix + End Sub + End Class + """; + + return VerifyVisualBasicAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(VisualBasicTypes))] + public Task VB_UseVolatileWrite(string type) + { + var code = $$""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {{type}}, value As {{type}}) + {|#0:Thread.VolatileWrite(arg, value)|} + End Sub + End Class + """; + var fixedCode = $""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {type}, value As {type}) + Volatile.Write(arg, value) + End Sub + End Class + """; + + return VerifyVisualBasicAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(VisualBasicTypes))] + public Task VB_UseVolatileWrite_WithNamedArguments(string type) + { + var code = $$""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {{type}}, value As {{type}}) + {|#0:Thread.VolatileWrite(address:=arg, value:=value)|} + End Sub + End Class + """; + var fixedCode = $""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {type}, value As {type}) + Volatile.Write(location:=arg, value:=value) + End Sub + End Class + """; + + return VerifyVisualBasicAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(VisualBasicTypes))] + public Task VB_UseVolatileWrite_WithReversedArguments(string type) + { + var code = $$""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {{type}}, value As {{type}}) + {|#0:Thread.VolatileWrite(value:=value, address:=arg)|} + End Sub + End Class + """; + var fixedCode = $""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {type}, value As {type}) + Volatile.Write(location:=arg, value:=value) + End Sub + End Class + """; + + return VerifyVisualBasicAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(VisualBasicTypes))] + public Task VB_UseVolatileWrite_WithSingleNamedArgument(string type) + { + var code = $$""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {{type}}, value As {{type}}) + {|#0:Thread.VolatileWrite(address:=arg, value)|} + End Sub + End Class + """; + var fixedCode = $""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {type}, value As {type}) + Volatile.Write(location:=arg, value) + End Sub + End Class + """; + + return VerifyVisualBasicAsync(code, fixedCode); + } + + [Theory] + [MemberData(nameof(VisualBasicTypes))] + public Task VB_UseVolatileWrite_WithTrivia(string type) + { + var code = $$""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {{type}}, value As {{type}}) + ' Trivia prefix + {|#0:Thread.VolatileWrite(arg, value)|} ' Trivia infix + ' Trivia suffix + End Sub + End Class + """; + var fixedCode = $""" + Imports System + Imports System.Threading + + Class Test + Sub M(arg As {type}, value As {type}) + ' Trivia prefix + Volatile.Write(arg, value) ' Trivia infix + ' Trivia suffix + End Sub + End Class + """; + + return VerifyVisualBasicAsync(code, fixedCode); + } + + private static Task VerifyCsharpAsync(string code, string fixedCode) + { + return new VerifyCS.Test + { + TestState = + { + Sources = { code, CsharpSystemThreadingThread } + }, + FixedState = + { + Sources = { fixedCode, CsharpSystemThreadingThread } + }, + ExpectedDiagnostics = + { + new DiagnosticResult("SYSLIB0054", DiagnosticSeverity.Warning).WithLocation(0) + }, + LanguageVersion = LanguageVersion.CSharp8, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }.RunAsync(); + } + + private static Task VerifyVisualBasicAsync(string code, string fixedCode) + { + return new VerifyVB.Test + { + TestState = + { + Sources = { code, VisualBasicSystemThreadingThread } + }, + FixedState = + { + Sources = { fixedCode, VisualBasicSystemThreadingThread } + }, + ExpectedDiagnostics = + { + new DiagnosticResult("SYSLIB0054", DiagnosticSeverity.Warning).WithLocation(0) + }, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }.RunAsync(); + } + } +} \ No newline at end of file diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicUseVolatileReadWriteFixer.vb b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicUseVolatileReadWriteFixer.vb new file mode 100644 index 0000000000..e9afa24dce --- /dev/null +++ b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicUseVolatileReadWriteFixer.vb @@ -0,0 +1,39 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +Imports System.Collections.Immutable +Imports System.Composition +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.NetCore.Analyzers.Usage + +Namespace Microsoft.NetCore.VisualBasic.Analyzers.Usage + + + Public NotInheritable Class BasicUseVolatileReadWriteFixer + Inherits UseVolatileReadWriteFixer + Protected Overrides Function GetArgumentForVolatileReadCall(argument As IArgumentOperation, volatileReadParameter as IParameterSymbol) As SyntaxNode + Dim argumentSyntax = DirectCast(argument.Syntax, SimpleArgumentSyntax) + If argumentSyntax.NameColonEquals Is Nothing Then + Return argumentSyntax + End If + + Return argumentSyntax.WithNameColonEquals(SyntaxFactory.NameColonEquals(SyntaxFactory.IdentifierName(volatileReadParameter.Name))) + End Function + + Protected Overrides Iterator Function GetArgumentForVolatileWriteCall(arguments As ImmutableArray(Of IArgumentOperation), volatileWriteParameters As ImmutableArray(Of IParameterSymbol)) As IEnumerable(Of SyntaxNode) + For Each argument In arguments + Dim argumentSyntax = DirectCast(argument.Syntax, SimpleArgumentSyntax) + If argumentSyntax.NameColonEquals Is Nothing Then + Yield argumentSyntax + Else + Dim parameterName = volatileWriteParameters(argument.Parameter.Ordinal).Name + Yield argumentSyntax.WithNameColonEquals(SyntaxFactory.NameColonEquals(SyntaxFactory.IdentifierName(parameterName))) + End If + Next + End Function + End Class + +End Namespace \ No newline at end of file diff --git a/src/Utilities/Compiler/WellKnownTypeNames.cs b/src/Utilities/Compiler/WellKnownTypeNames.cs index b85343f7e6..9433ed1078 100644 --- a/src/Utilities/Compiler/WellKnownTypeNames.cs +++ b/src/Utilities/Compiler/WellKnownTypeNames.cs @@ -439,6 +439,7 @@ internal static class WellKnownTypeNames public const string SystemThreadingTasksValueTask = "System.Threading.Tasks.ValueTask"; public const string SystemThreadingTasksValueTask1 = "System.Threading.Tasks.ValueTask`1"; public const string SystemThreadingThread = "System.Threading.Thread"; + public const string SystemThreadingVolatile = "System.Threading.Volatile"; public const string SystemTimeSpan = "System.TimeSpan"; public const string SystemType = "System.Type"; public const string SystemUri = "System.Uri";