diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index 5df32136db4da..75216a18d444c 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -7824,4 +7824,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
This version of '{0}' cannot be used with collection expressions.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
\ 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 ae4ce264ddfb0..53c38c18a4fbd 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
@@ -2279,6 +2279,8 @@ internal enum ErrorCode
WRN_CollectionExpressionRefStructSpreadMayAllocate = 9209,
ERR_CollectionExpressionImmutableArray = 9210,
+ ERR_InvalidExperimentalDiagID = 9211,
+
#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 2e868d43cfef9..bfd633ae39c19 100644
--- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
+++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs
@@ -2409,6 +2409,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code)
case ErrorCode.WRN_CollectionExpressionRefStructMayAllocate:
case ErrorCode.WRN_CollectionExpressionRefStructSpreadMayAllocate:
case ErrorCode.ERR_CollectionExpressionImmutableArray:
+ case ErrorCode.ERR_InvalidExperimentalDiagID:
return false;
default:
// NOTE: All error codes must be explicitly handled in this switch statement
diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs
index bf58db1cbede2..3f7788cec1069 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs
@@ -320,23 +320,11 @@ internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
{
if (_lazyObsoleteAttributeData == ObsoleteAttributeData.Uninitialized)
{
- Interlocked.CompareExchange(ref _lazyObsoleteAttributeData, computeObsoleteAttributeData(), ObsoleteAttributeData.Uninitialized);
+ var experimentalData = PrimaryModule.Module.TryDecodeExperimentalAttributeData(Assembly.Handle, new MetadataDecoder(PrimaryModule));
+ Interlocked.CompareExchange(ref _lazyObsoleteAttributeData, experimentalData, ObsoleteAttributeData.Uninitialized);
}
return _lazyObsoleteAttributeData;
-
- ObsoleteAttributeData? computeObsoleteAttributeData()
- {
- foreach (var attrData in GetAttributes())
- {
- if (attrData.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute))
- {
- return attrData.DecodeExperimentalAttribute();
- }
- }
-
- return null;
- }
}
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs
index 472aa557609d0..8a2c938916106 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs
@@ -866,23 +866,11 @@ internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
{
if (_lazyObsoleteAttributeData == ObsoleteAttributeData.Uninitialized)
{
- Interlocked.CompareExchange(ref _lazyObsoleteAttributeData, computeObsoleteAttributeData(), ObsoleteAttributeData.Uninitialized);
+ var experimentalData = _module.TryDecodeExperimentalAttributeData(Token, new MetadataDecoder(this));
+ Interlocked.CompareExchange(ref _lazyObsoleteAttributeData, experimentalData, ObsoleteAttributeData.Uninitialized);
}
return _lazyObsoleteAttributeData;
-
- ObsoleteAttributeData? computeObsoleteAttributeData()
- {
- foreach (var attrData in GetAttributes())
- {
- if (attrData.IsTargetAttribute(this, AttributeDescription.ExperimentalAttribute))
- {
- return attrData.DecodeExperimentalAttribute();
- }
- }
-
- return null;
- }
}
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs
index b48bc91efbdcc..04f4b03221948 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs
@@ -223,7 +223,14 @@ protected void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgumentsPřevody interpolovaných obslužných rutin řetězců, které odkazují na indexovanou instanci, se nedají použít v inicializátorech členů indexeru.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.{0} není platný modifikátor návratového typu ukazatele na funkci. Platné modifikátory jsou ref a ref readonly.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
index 4e3a26c36a90c..451404b03849c 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
@@ -1122,6 +1122,11 @@
Handler-Konvertierungen interpolierter Zeichenfolgen die auf die Instanz verweisen, die gerade indiziert wird, können nicht in Indexer-Member-Initialisierern verwendet werden.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'."{0}" ist kein gültiger Rückgabetyp-Modifizierer für Funktionszeiger. Gültige Modifizierer sind "ref" und "ref readonly".
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
index e5a2371f66539..433a789b0814d 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
@@ -1122,6 +1122,11 @@
Las conversiones de controlador de cadenas interpoladas que hacen referencia a la instancia que se está indizando no se puede usar en inicializadores de miembros de indizador.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'."{0}" no es un modificador de tipo de valor devuelto de puntero de función válido. Los modificadores válidos son "ref" y "ref readonly".
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
index c79ed76f8f413..7d611ca27dc02 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
@@ -1122,6 +1122,11 @@
Les conversions de gestionnaires de chaînes interpolées qui font référence à l'instance en cours d'indexation ne peuvent pas être utilisées dans les initialiseurs de membres d'indexeur.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.'{0}' n'est pas un modificateur de type de retour de pointeur de fonction valide. Les modificateurs valides sont 'ref' et 'ref readonly'.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
index 7263f02bb1393..4f2d8ab977cd2 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
@@ -1122,6 +1122,11 @@
Le conversioni di gestori di stringhe interpolate che fanno riferimento all'istanza indicizzata non possono essere utilizzate negli inizializzatori di membri dell'indicizzatore.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.'{0}' non è un modificatore di tipo restituito di puntatore a funzione valido. I modificatori validi sono 'ref' e 'ref readonly'.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
index caa7317d5094a..783b1fc8213ce 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
@@ -1122,6 +1122,11 @@
インデックス付けされているインスタンスを参照する補間された文字列ハンドラーの変換は、インデクサー メンバー初期化子では使用できません。
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.'{0}' は有効な関数ポインターの戻り値の型修飾子ではありません。有効な修飾子は 'ref ' および 'ref readonly' です。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
index ea27e1171a864..60030b14884cc 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
@@ -1122,6 +1122,11 @@
인덱싱되는 인스턴스를 참조하는 보간된 문자열 처리기 변환은 인덱서 멤버 이니셜라이저에서 사용할 수 없습니다.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.'{0}'은(는) 유효한 함수 포인터 반환 형식 한정자가 아닙니다. 유효한 한정자는 'ref' 및 'ref readonly'입니다.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
index 96943882e0e4e..52713880a3339 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
@@ -1122,6 +1122,11 @@
W inicjatorach składowych indeksatora nie można używać konwersji procedury obsługi ciągów interpolowanych odwołujących się do indeksowanego wystąpienia.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.Element „{0}” nie jest prawidłowym modyfikatorem zwracanego typu wskaźnikowego funkcji. Prawidłowe modyfikatory to „ref” i „ref readonly”.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
index 264607a200f35..2279017bef62a 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
@@ -1122,6 +1122,11 @@
Conversões do manipulador de cadeia de caracteres interpoladas que fazem referência à instância que está sendo indexada não podem ser usadas em inicializadores de membros indexadores.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.'{0}' não é um modificador de tipo de retorno de ponteiro de função válido. Os modificadores válidos são 'ref' e 'ref readonly'.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
index 46b5b4dc2cdd9..819034b56dbef 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
@@ -1122,6 +1122,11 @@
Преобразования обработчика интерполированной строки, ссылающиеся на индексируемый экземпляр, нельзя использовать в инициализаторах элементов индексатора.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'."{0}" не является допустимым модификатором типа для возвращаемого значения указателя на функцию. Допустимые модификаторы: ref и ref readonly.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
index 6c8b4d81c2fc2..6c51ad09f76d0 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
@@ -1122,6 +1122,11 @@
Dizine alınan örneğe başvurulan düz metin arasına kod ekli dize işleyici dönüştürmeleri, dizin oluşturucu üye başlatıcılarında kullanılamaz.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.'{0}', geçerli bir işlev işaretçisi dönüş türü değiştiricisi değil. Geçerli değiştiriciler: 'ref' ve 'ref readonly'.
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
index 276633bd835a2..efffbfd9f1c60 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
@@ -1122,6 +1122,11 @@
引用要编制索引的实例的内插字符串处理程序转换不能用于索引器成员初始化表达式中。
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.“{0}”不是有效的函数指针返回类型修饰符。有效的修饰符为 "ref" 和 "ref readonly"。
diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
index 1bc2dab79b57b..0dcf83bc4947c 100644
--- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
+++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
@@ -1122,6 +1122,11 @@
參考要編制索引的執行個體的差補字串處理常式轉換無法用於索引子成員初始化程式。
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'.'{0}'不是有效的函式指標傳回型別修飾元。有效的修飾元為 'ref' 與 'ref readonly'。
diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
index 3c69299978022..084e2adc33cf7 100644
--- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
+++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs
@@ -14875,6 +14875,125 @@ public static void M() { }
var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithGeneralDiagnosticOption(ReportDiagnostic.Suppress));
comp.VerifyDiagnostics();
}
+
+ [Fact]
+ public void ExperimentalWithWhitespaceDiagnosticID_WarnForInvalidDiagID()
+ {
+ var dir = Temp.CreateDirectory();
+ var src = dir.CreateFile("test.cs").WriteAllText("""
+C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental(" ")]
+public class C
+{
+ public static void M() { }
+}
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ public sealed class ExperimentalAttribute : Attribute
+ {
+ public ExperimentalAttribute(string diagnosticId) { }
+ }
+}
+""");
+ var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
+[*.cs]
+dotnet_diagnostic.CS9211.severity = warning
+""");
+ Assert.Equal((ErrorCode)9211, ErrorCode.ERR_InvalidExperimentalDiagID);
+ var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
+ "/nologo",
+ "/t:exe",
+ "/preferreduilang:en",
+ "/analyzerconfig:" + analyzerConfig.Path,
+ src.Path });
+
+ var outWriter = new StringWriter(CultureInfo.InvariantCulture);
+ var exitCode = cmd.Run(outWriter);
+ Assert.Equal(1, exitCode);
+ Assert.StartsWith("test.cs(3,47): error CS9211: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier",
+ outWriter.ToString());
+ }
+
+ [Fact]
+ public void ExperimentalWithValidDiagnosticID_WarnForDiagID()
+ {
+ var dir = Temp.CreateDirectory();
+ var src = dir.CreateFile("test.cs").WriteAllText("""
+C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental("DiagID")]
+public class C
+{
+ public static void M() { }
+}
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ public sealed class ExperimentalAttribute : Attribute
+ {
+ public ExperimentalAttribute(string diagnosticId) { }
+ }
+}
+""");
+ var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
+[*.cs]
+dotnet_diagnostic.DiagID.severity = warning
+""");
+ var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
+ "/nologo",
+ "/t:exe",
+ "/preferreduilang:en",
+ "/analyzerconfig:" + analyzerConfig.Path,
+ src.Path });
+
+ var outWriter = new StringWriter(CultureInfo.InvariantCulture);
+ var exitCode = cmd.Run(outWriter);
+ Assert.Equal(0, exitCode);
+ Assert.StartsWith("test.cs(1,1): warning DiagID: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.",
+ outWriter.ToString());
+ }
+
+ [Fact]
+ public void ExperimentalWithValidDiagnosticID_WarnForExperimental()
+ {
+ var dir = Temp.CreateDirectory();
+ var src = dir.CreateFile("test.cs").WriteAllText("""
+C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental("DiagID")]
+public class C
+{
+ public static void M() { }
+}
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ public sealed class ExperimentalAttribute : Attribute
+ {
+ public ExperimentalAttribute(string diagnosticId) { }
+ }
+}
+""");
+ var analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("""
+[*.cs]
+dotnet_diagnostic.CS9204.severity = warning
+""");
+ Assert.Equal((ErrorCode)9204, ErrorCode.WRN_Experimental);
+ var cmd = CreateCSharpCompiler(null, dir.Path, new[] {
+ "/nologo",
+ "/t:exe",
+ "/preferreduilang:en",
+ "/analyzerconfig:" + analyzerConfig.Path,
+ src.Path });
+
+ var outWriter = new StringWriter(CultureInfo.InvariantCulture);
+ var exitCode = cmd.Run(outWriter);
+ Assert.Equal(1, exitCode);
+ Assert.StartsWith("test.cs(1,1): error DiagID: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.",
+ outWriter.ToString());
+ }
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs
index 069d431733836..a2653bcb507ed 100644
--- a/src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs
+++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ExperimentalAttributeTests.cs
@@ -1368,8 +1368,8 @@ public class C<[System.Diagnostics.CodeAnalysis.Experimental("DiagID1")] T> { }
comp.VerifyDiagnostics();
}
- [Theory, CombinatorialData]
- public void NullDiagnosticId(bool inSource)
+ [Fact]
+ public void NullDiagnosticId()
{
var libSrc = """
[System.Diagnostics.CodeAnalysis.Experimental(null)]
@@ -1382,19 +1382,82 @@ public static void M() { }
var src = """
C.M();
""";
- var comp = inSource
- ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
- : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+ var comp = CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc });
comp.VerifyDiagnostics(
- // 0.cs(1,1): error CS9204: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // 0.cs(1,1): error CS9204: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
// C.M();
- Diagnostic(ErrorCode.WRN_Experimental, "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true)
+ Diagnostic(ErrorCode.WRN_Experimental, "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true),
+ // 1.cs(1,47): error CS9208: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ // [System.Diagnostics.CodeAnalysis.Experimental(null)]
+ Diagnostic(ErrorCode.ERR_InvalidExperimentalDiagID, "null").WithLocation(1, 47)
);
}
- [Theory, CombinatorialData]
- public void DiagnosticIdWithTrailingNewline(bool inSource)
+ [Fact]
+ public void MissingDiagnosticIdArgument()
+ {
+ var src = """
+C.M();
+[System.Diagnostics.CodeAnalysis.Experimental()]
+public class C
+{
+ public static void M() { }
+}
+""";
+ var comp = CreateCompilation(new[] { src, experimentalAttributeSrc });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(2,2): error CS7036: There is no argument given that corresponds to the required parameter 'diagnosticId' of 'ExperimentalAttribute.ExperimentalAttribute(string)'
+ // [System.Diagnostics.CodeAnalysis.Experimental()]
+ Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "System.Diagnostics.CodeAnalysis.Experimental()").WithArguments("diagnosticId", "System.Diagnostics.CodeAnalysis.ExperimentalAttribute.ExperimentalAttribute(string)").WithLocation(2, 2)
+ );
+ }
+
+ [Fact]
+ public void IntegerDiagnosticIdArgument()
+ {
+ var src = """
+C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental(42)]
+public class C
+{
+ public static void M() { }
+}
+""";
+ var comp = CreateCompilation(new[] { src, experimentalAttributeSrc });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(3,47): error CS1503: Argument 1: cannot convert from 'int' to 'string'
+ // [System.Diagnostics.CodeAnalysis.Experimental(42)]
+ Diagnostic(ErrorCode.ERR_BadArgType, "42").WithArguments("1", "int", "string").WithLocation(3, 47)
+ );
+ }
+
+ [Fact]
+ public void MultipleArguments()
+ {
+ var src = """
+C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental("DiagID", "other")]
+public class C
+{
+ public static void M() { }
+}
+""";
+ var comp = CreateCompilation(new[] { src, experimentalAttributeSrc });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(3,2): error CS1729: 'ExperimentalAttribute' does not contain a constructor that takes 2 arguments
+ // [System.Diagnostics.CodeAnalysis.Experimental("DiagID", "other")]
+ Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"System.Diagnostics.CodeAnalysis.Experimental(""DiagID"", ""other"")").WithArguments("System.Diagnostics.CodeAnalysis.ExperimentalAttribute", "2").WithLocation(3, 2)
+ );
+ }
+
+ [Fact]
+ public void DiagnosticIdWithTrailingNewline()
{
var libSrc = """
[System.Diagnostics.CodeAnalysis.Experimental("Diag\n")]
@@ -1407,45 +1470,70 @@ public static void M() { }
var src = """
C.M();
""";
- var comp = inSource
- ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
- : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+ var comp = CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc });
comp.VerifyDiagnostics(
- // 0.cs(1,1): error Diag : 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // 0.cs(1,1): error Diag\n: 'C' is for evaluation purposes only and is subject to change or removal in future updates.Suppress this diagnostic to proceed.
// C.M();
- Diagnostic("Diag\n", "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true)
+ Diagnostic("Diag\n", "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true),
+ // 1.cs(1,47): error CS9208: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ // [System.Diagnostics.CodeAnalysis.Experimental("Diag\n")]
+ Diagnostic(ErrorCode.ERR_InvalidExperimentalDiagID, @"""Diag\n""").WithLocation(1, 47)
);
}
- [Theory, CombinatorialData]
- public void DiagnosticIdWithNewline(bool inSource)
+ [Fact]
+ public void DiagnosticIdWithTrailingNewline_WithSuppression()
{
- var libSrc = """
-[System.Diagnostics.CodeAnalysis.Experimental("Diag\n01")]
+ var src = """
+#pragma warning disable CS9204
+C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental("Diag\n")]
public class C
{
public static void M() { }
}
""";
+ Assert.Equal((ErrorCode)9204, ErrorCode.WRN_Experimental);
+ var comp = CreateCompilation(new[] { src, experimentalAttributeSrc });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(2,1): error Diag\n: 'C' is for evaluation purposes only and is subject to change or removal in future updates.Suppress this diagnostic to proceed.
+ // C.M();
+ Diagnostic("Diag\n", "C").WithArguments("C").WithLocation(2, 1).WithWarningAsError(true),
+ // 0.cs(4,47): error CS9211: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ // [System.Diagnostics.CodeAnalysis.Experimental("Diag\n")]
+ Diagnostic(ErrorCode.ERR_InvalidExperimentalDiagID, @"""Diag\n""").WithLocation(4, 47)
+ );
+ }
+ [Fact]
+ public void DiagnosticIdWithNewline()
+ {
var src = """
C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental("Diag\n01")]
+public class C
+{
+ public static void M() { }
+}
""";
- var comp = inSource
- ? CreateCompilation(new[] { src, libSrc, experimentalAttributeSrc })
- : CreateCompilation(src, references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+ var comp = CreateCompilation(new[] { src, experimentalAttributeSrc });
comp.VerifyDiagnostics(
- // 0.cs(1,1): error Diag 01: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // 0.cs(1,1): error Diag\n01: 'C' is for evaluation purposes only and is subject to change or removal in future updates.Suppress this diagnostic to proceed.
// C.M();
- Diagnostic("Diag\n01", "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true)
+ Diagnostic("Diag\n01", "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true),
+ // 0.cs(3,47): error CS9208: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ // [System.Diagnostics.CodeAnalysis.Experimental("Diag\n01")]
+ Diagnostic(ErrorCode.ERR_InvalidExperimentalDiagID, @"""Diag\n01""").WithLocation(3, 47)
);
}
[Theory, CombinatorialData]
- public void WhitespaceDiagnosticId(bool inSource,
- [CombinatorialValues("\"\"", "\" \"", "\"\\n\"")] string whitespace)
+ public void WhitespaceDiagnosticId([CombinatorialValues("\"\"", "\" \"", "\"\\n\"")] string whitespace)
{
var libSrc = $$"""
[System.Diagnostics.CodeAnalysis.Experimental({{whitespace}})]
@@ -1459,14 +1547,38 @@ public static void M() { }
C.M();
""";
- var comp = inSource
- ? CreateCompilation(new CSharpTestSource[] { (src, "0.cs"), libSrc, experimentalAttributeSrc })
- : CreateCompilation((src, "0.cs"), references: new[] { CreateCompilation(new[] { libSrc, experimentalAttributeSrc }).EmitToImageReference() });
+ var comp = CreateCompilation(new CSharpTestSource[] { (src, "0.cs"), libSrc, experimentalAttributeSrc });
comp.VerifyDiagnostics(
- // 0.cs(1,1): error CS9204: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // 0.cs(1,1): error CS9204: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
// C.M();
- Diagnostic(ErrorCode.WRN_Experimental, "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true)
+ Diagnostic(ErrorCode.WRN_Experimental, "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true),
+ // (1,47): error CS9208: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ // [System.Diagnostics.CodeAnalysis.Experimental(" ")]
+ Diagnostic(ErrorCode.ERR_InvalidExperimentalDiagID, whitespace).WithLocation(1, 47)
+ );
+ }
+
+ [Fact]
+ public void WhitespaceDiagnosticId_WithSuppression()
+ {
+ var src = """
+#pragma warning disable CS9204
+C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental(" ")]
+public class C
+{
+ public static void M() { }
+}
+""";
+
+ var comp = CreateCompilation(new CSharpTestSource[] { (src, "0.cs"), experimentalAttributeSrc });
+
+ comp.VerifyDiagnostics(
+ // 0.cs(4,47): error CS9211: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ // [System.Diagnostics.CodeAnalysis.Experimental(" ")]
+ Diagnostic(ErrorCode.ERR_InvalidExperimentalDiagID, @""" """).WithLocation(4, 47)
);
}
@@ -1484,10 +1596,89 @@ public static void M() { }
""";
var comp = CreateCompilation(new[] { src, experimentalAttributeSrc });
comp.VerifyDiagnostics(
- // 0.cs(1,1): error Diag 01: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ // 0.cs(1,1): error Diag 01: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+ // C.M();
+ Diagnostic("Diag 01", "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true),
+ // 0.cs(3,47): error CS9208: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ // [System.Diagnostics.CodeAnalysis.Experimental("Diag 01")]
+ Diagnostic(ErrorCode.ERR_InvalidExperimentalDiagID, @"""Diag 01""").WithLocation(3, 47)
+ );
+ }
+
+ [Fact]
+ public void SpacedDiagnosticId_WithSecondArgument()
+ {
+ var src = """
+C.M();
+
+[System.Diagnostics.CodeAnalysis.Experimental("Diag 01", "other")]
+class C
+{
+ public static void M() { }
+}
+""";
+ var comp = CreateCompilation(new[] { src, experimentalAttributeSrc });
+ comp.VerifyDiagnostics(
+ // 0.cs(3,2): error CS1729: 'ExperimentalAttribute' does not contain a constructor that takes 2 arguments
+ // [System.Diagnostics.CodeAnalysis.Experimental("Diag 01", "other")]
+ Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"System.Diagnostics.CodeAnalysis.Experimental(""Diag 01"", ""other"")").WithArguments("System.Diagnostics.CodeAnalysis.ExperimentalAttribute", "2").WithLocation(3, 2)
+ );
+ }
+
+ [Fact]
+ public void SpacedDiagnosticId_Metadata()
+ {
+ var il = """
+.class public auto ansi beforefieldinit C
+ extends [mscorlib]System.Object
+{
+ .custom instance void System.Diagnostics.CodeAnalysis.ExperimentalAttribute::.ctor(string) = { string('Diag 01') }
+
+ .method public hidebysig static void M () cil managed
+ {
+ IL_0000: ret
+ }
+
+ .method public hidebysig specialname rtspecialname instance void .ctor () cil managed
+ {
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ }
+}
+
+.class public auto ansi sealed beforefieldinit System.Diagnostics.CodeAnalysis.ExperimentalAttribute
+ extends [mscorlib]System.Attribute
+{
+ .method public hidebysig specialname rtspecialname instance void .ctor ( string diagnosticId ) cil managed
+ {
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Attribute::.ctor()
+ IL_0006: ret
+ }
+}
+""";
+ var comp = CreateCompilationWithIL("C.M();", il);
+ comp.VerifyDiagnostics(
+ // (1,1): error Diag 01: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
// C.M();
Diagnostic("Diag 01", "C").WithArguments("C").WithLocation(1, 1).WithWarningAsError(true)
);
+
+ var src = """
+// Cannot be suppressed
+#pragma disable warning Diag 01
+C.M();
+""";
+ comp = CreateCompilationWithIL(src, il);
+ comp.VerifyDiagnostics(
+ // (2,9): warning CS1633: Unrecognized #pragma directive
+ // #pragma disable warning Diag 01
+ Diagnostic(ErrorCode.WRN_IllegalPragma, "disable").WithLocation(2, 9),
+ // (3,1): error Diag 01: 'C' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+ // C.M();
+ Diagnostic("Diag 01", "C").WithArguments("C").WithLocation(3, 1).WithWarningAsError(true)
+ );
}
[Fact]
diff --git a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs
index 56794806b2e7c..3cf621408e0b7 100644
--- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs
+++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs
@@ -1227,6 +1227,12 @@ internal ObsoleteAttributeData TryGetDeprecatedOrExperimentalOrObsoleteAttribute
}
#nullable enable
+ internal ObsoleteAttributeData? TryDecodeExperimentalAttributeData(EntityHandle handle, IAttributeNamedArgumentDecoder decoder)
+ {
+ var info = FindTargetAttribute(handle, AttributeDescription.ExperimentalAttribute);
+ return info.HasValue ? TryExtractExperimentalDataFromAttribute(info, decoder) : null;
+ }
+
private ObsoleteAttributeData? TryExtractExperimentalDataFromAttribute(AttributeInfo attributeInfo, IAttributeNamedArgumentDecoder decoder)
{
Debug.Assert(attributeInfo.HasValue);
diff --git a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb
index c11ef619b2bec..d8e41bd58db71 100644
--- a/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb
+++ b/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb
@@ -1536,7 +1536,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
ERRID.WRN_CallerArgumentExpressionAttributeSelfReferential,
ERRID.WRN_CallerArgumentExpressionAttributeHasInvalidParameterName,
ERRID.WRN_AnalyzerReferencesNewerCompiler,
- ERRID.WRN_DuplicateAnalyzerReference
+ ERRID.WRN_DuplicateAnalyzerReference,
+ ERRID.ERR_InvalidExperimentalDiagID
Return False
Case Else
' NOTE: All error codes must be explicitly handled in the below select case statement
diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb
index d440070420d43..6104e6c691811 100644
--- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb
+++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb
@@ -1776,7 +1776,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
ERR_UnsupportedRefReturningCallInWithStatement = 37326
ERR_SymbolDefinedInAssembly = 37327
- ERR_NextAvailable = 37328
+ ERR_InvalidExperimentalDiagID = 37328
+
+ ERR_NextAvailable = 37329
'// WARNINGS BEGIN HERE
WRN_UseOfObsoleteSymbol2 = 40000
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb
index 5a8a17aab70d1..25b289e04eed5 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb
@@ -292,22 +292,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
Get
If _lazyObsoleteAttributeData Is ObsoleteAttributeData.Uninitialized Then
- Interlocked.CompareExchange(_lazyObsoleteAttributeData, ComputeObsoleteAttributeData(), ObsoleteAttributeData.Uninitialized)
+ Dim experimentalData = PrimaryModule.Module.TryDecodeExperimentalAttributeData(Assembly.Handle, New MetadataDecoder(PrimaryModule))
+ Interlocked.CompareExchange(_lazyObsoleteAttributeData, experimentalData, ObsoleteAttributeData.Uninitialized)
End If
Return _lazyObsoleteAttributeData
End Get
End Property
- Private Function ComputeObsoleteAttributeData() As ObsoleteAttributeData
- For Each attrData In GetAttributes()
- If attrData.IsTargetAttribute(Me, AttributeDescription.ExperimentalAttribute) Then
- Return attrData.DecodeExperimentalAttribute()
- End If
- Next
-
- Return Nothing
- End Function
-
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEModuleSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEModuleSymbol.vb
index 40b385c28a0b5..7a54997c9667d 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEModuleSymbol.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEModuleSymbol.vb
@@ -513,22 +513,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Friend Overrides ReadOnly Property ObsoleteAttributeData As ObsoleteAttributeData
Get
If _lazyObsoleteAttributeData Is ObsoleteAttributeData.Uninitialized Then
- Interlocked.CompareExchange(_lazyObsoleteAttributeData, ComputeObsoleteAttributeData(), ObsoleteAttributeData.Uninitialized)
+ Dim experimentalData = _module.TryDecodeExperimentalAttributeData(EntityHandle.ModuleDefinition, New MetadataDecoder(Me))
+ Interlocked.CompareExchange(_lazyObsoleteAttributeData, experimentalData, ObsoleteAttributeData.Uninitialized)
End If
Return _lazyObsoleteAttributeData
End Get
End Property
- Private Function ComputeObsoleteAttributeData() As ObsoleteAttributeData
- For Each attrData In GetAttributes()
- If attrData.IsTargetAttribute(Me, AttributeDescription.ExperimentalAttribute) Then
- Return attrData.DecodeExperimentalAttribute()
- End If
- Next
-
- Return Nothing
- End Function
-
End Class
End Namespace
diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb b/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb
index 9ac12b0737505..0e013a64d1e72 100644
--- a/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb
+++ b/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb
@@ -204,6 +204,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
DirectCast(arguments.Diagnostics, BindingDiagnosticBag).Add(ERRID.ERR_DoNotUseCompilerFeatureRequired, arguments.AttributeSyntaxOpt.Location)
ElseIf arguments.Attribute.IsTargetAttribute(Me, AttributeDescription.RequiredMemberAttribute) Then
DirectCast(arguments.Diagnostics, BindingDiagnosticBag).Add(ERRID.ERR_DoNotUseRequiredMember, arguments.AttributeSyntaxOpt.Location)
+ ElseIf arguments.Attribute.IsTargetAttribute(Me, AttributeDescription.ExperimentalAttribute) Then
+ If Not SyntaxFacts.IsValidIdentifier(DirectCast(arguments.Attribute.CommonConstructorArguments(0).ValueInternal, String)) Then
+ Dim attrArgumentLocation = VisualBasicAttributeData.GetFirstArgumentLocation(arguments.AttributeSyntaxOpt)
+ DirectCast(arguments.Diagnostics, BindingDiagnosticBag).Add(ERRID.ERR_InvalidExperimentalDiagID, attrArgumentLocation)
+ End If
End If
End Sub
diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx
index ccb787f17d821..62fb8733c2c40 100644
--- a/src/Compilers/VisualBasic/Portable/VBResources.resx
+++ b/src/Compilers/VisualBasic/Portable/VBResources.resx
@@ -5692,4 +5692,7 @@
'{0}' is defined in assembly '{1}'.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf
index 086ce5a74c4f4..ff0ecc33800d2 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf
@@ -42,6 +42,11 @@
Atribut System.Runtime.CompilerServices.RequiredMemberAttribute je vyhrazen pouze pro použití kompilátoru.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').Ve stejném adresáři nemůže být více konfiguračních souborů analyzátoru ({0}).
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf
index 0bda8749e89a8..00d378a6c8075 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf
@@ -42,6 +42,11 @@
"System.Runtime.CompilerServices.RequiredMemberAttribute" ist nur für die Compilerverwendung reserviert.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').Dasselbe Verzeichnis ({0}) darf nicht mehrere Konfigurationsdateien des Analysetools enthalten.
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf
index ca8ff2ca5b93e..5555750ee95d4 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf
@@ -42,6 +42,11 @@
'System.Runtime.CompilerServices.RequiredMemberAttribute' está reservado solo para uso del compilador.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').No es posible que un mismo directorio ("{0}") contenga varios archivos de configuración del analizador.
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf
index 44022eebcc67a..44ed8139728ee 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf
@@ -42,6 +42,11 @@
'System.Runtime.CompilerServices.RequiredMemberAttribute' est réservé à l'usage du compilateur uniquement.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').Plusieurs fichiers config d'analyseur ne peuvent pas figurer dans le même répertoire ('{0}').
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf
index 5d78a6be1c449..bc311fbaa3e6f 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf
@@ -42,6 +42,11 @@
'System.Runtime.CompilerServices.RequiredMemberAttribute' è riservato solo all’uso del compilatore.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').La stessa directory ('{0}') non può contenere più file di configurazione dell'analizzatore.
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf
index bde2c25481fd1..4aacd8a070373 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf
@@ -42,6 +42,11 @@
'System.Runtime.CompilerServices.RequiredMemberAttribute' は、コンパイラでのみ使用するために予約されています。
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').複数のアナライザー構成ファイルを同じディレクトリに入れることはできません ('{0}')。
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf
index 7cea213896df8..a22283a14f009 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf
@@ -42,6 +42,11 @@
'System.Runtime.CompilerServices.RequiredMemberAttribute'는 컴파일러 용도로만 예약되어 있습니다.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').분석기 구성 파일 여러 개가 동일한 디렉터리('{0}')에 있을 수 없습니다.
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf
index 08cac9d4787c2..21262d9af44a5 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf
@@ -42,6 +42,11 @@
Element „System.Runtime.CompilerServices.RequiredMemberAttribute” jest zarezerwowany tylko do użycia przez kompilator.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').Wiele plików konfiguracji analizatora nie może znajdować się w tym samym katalogu („{0}”).
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf
index 61cf74c6bf189..134d2cbc8c775 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf
@@ -42,6 +42,11 @@
'System.Runtime.CompilerServices.RequiredMemberAttribute' está reservado somente para uso do compilador.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').Não é possível que haja vários arquivos de configuração do analisador no mesmo diretório ('{0}').
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf
index dd5b9b9fc8ed7..3cdfa3530009e 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf
@@ -42,6 +42,11 @@
"System.Runtime.CompilerServices.RequiredMemberAttribute" зарезервирован только для использования компилятором.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').В одном каталоге ("{0}") не может находиться несколько файлов конфигурации анализатора.
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf
index d21bb86781780..e99fd1142bc4f 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf
@@ -42,6 +42,11 @@
'System.Runtime.CompilerServices.RequiredMemberAttribute' yalnızca derleyici kullanımı için ayrılmıştır.
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').Birden çok çözümleyici yapılandırma dosyası aynı dizinde ('{0}') olamaz.
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf
index 5468404306ff5..7f8917c112e7e 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf
@@ -42,6 +42,11 @@
"System.Runtime.CompilerServices.RequiredMemberAttribute" 仅供编译器使用。
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').多个分析器配置文件不能位于同一目录({0})中。
diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf
index c4a1ceed5d503..1991dff7e22d3 100644
--- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf
+++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf
@@ -42,6 +42,11 @@
'System.Runtime.CompilerServices.RequiredMemberAttribute' 僅保留給編譯器使用。
+
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+ The diagnosticId argument to the 'Experimental' attribute must be a valid identifier
+
+ Multiple analyzer config files cannot be in the same directory ('{0}').多個分析器組態檔無法處於相同目錄 ('{0}') 中。
diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb
index b0e3b9affe360..414e9fca64903 100644
--- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb
+++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb
@@ -10621,6 +10621,140 @@ End Class")
Assert.Equal(absPath, parsedArgs.GeneratedFilesOutputDirectory)
End Sub
+
+ Public Sub ExperimentalWithWhitespaceDiagnosticID_WarnForInvalidDiagID()
+ Dim dir = Temp.CreateDirectory()
+
+ Dim src = dir.CreateFile("test.vb").WriteAllText("
+Public Class D
+ Public Sub M(c As C)
+ End Sub
+End Class
+
+
+Public Class C
+ Public Shared Sub M()
+ End Sub
+End Class
+
+Namespace System.Diagnostics.CodeAnalysis
+ Public NotInheritable Class ExperimentalAttribute
+ Inherits Attribute
+
+ Public Sub New(ByVal diagnosticId As String)
+ End Sub
+ End Class
+End Namespace
+")
+
+ Dim analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("
+[*.vb]
+dotnet_diagnostic.BC37328.severity = warning")
+
+ Assert.Equal(DirectCast(37328, ERRID), ERRID.ERR_InvalidExperimentalDiagID)
+
+ Dim cmd = New MockVisualBasicCompiler(Nothing, dir.Path, {
+ "/nologo",
+ "/t:library",
+ "/preferreduilang:en",
+ "/analyzerconfig:" + analyzerConfig.Path,
+ src.Path})
+
+ Dim outWriter = New StringWriter(CultureInfo.InvariantCulture)
+ Dim exitCode = cmd.Run(outWriter)
+ Assert.Equal(1, exitCode)
+ Assert.Contains("error BC37328: The diagnosticId argument to the 'Experimental' attribute must be a valid identifier", outWriter.ToString())
+ End Sub
+
+
+ Public Sub ExperimentalWithValidDiagnosticID_WarnForDiagID()
+ Dim dir = Temp.CreateDirectory()
+
+ Dim src = dir.CreateFile("test.vb").WriteAllText("
+Public Class D
+ Public Sub M(c As C)
+ End Sub
+End Class
+
+
+Public Class C
+ Public Shared Sub M()
+ End Sub
+End Class
+
+Namespace System.Diagnostics.CodeAnalysis
+ Public NotInheritable Class ExperimentalAttribute
+ Inherits Attribute
+
+ Public Sub New(ByVal diagnosticId As String)
+ End Sub
+ End Class
+End Namespace
+")
+
+ Dim analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("
+[*.vb]
+dotnet_diagnostic.DiagID.severity = warning")
+
+ Dim cmd = New MockVisualBasicCompiler(Nothing, dir.Path, {
+ "/nologo",
+ "/t:library",
+ "/preferreduilang:en",
+ "/analyzerconfig:" + analyzerConfig.Path,
+ src.Path})
+
+ Dim outWriter = New StringWriter(CultureInfo.InvariantCulture)
+ Dim exitCode = cmd.Run(outWriter)
+ Assert.Equal(0, exitCode)
+ Assert.Contains("warning DiagID: 'C' is for evaluation purposes only and is subject to change or removal in future updates.", outWriter.ToString())
+ End Sub
+
+
+ Public Sub ExperimentalWithValidDiagnosticID_WarnForExperimental()
+ Dim dir = Temp.CreateDirectory()
+
+ Dim src = dir.CreateFile("test.vb").WriteAllText("
+Public Class D
+ Public Sub M(c As C)
+ End Sub
+End Class
+
+
+Public Class C
+ Public Shared Sub M()
+ End Sub
+End Class
+
+Namespace System.Diagnostics.CodeAnalysis
+ Public NotInheritable Class ExperimentalAttribute
+ Inherits Attribute
+
+ Public Sub New(ByVal diagnosticId As String)
+ End Sub
+ End Class
+End Namespace
+")
+
+ Dim analyzerConfig = dir.CreateFile(".editorconfig").WriteAllText("
+[*.vb]
+dotnet_diagnostic.BC42380.severity = warning")
+
+ Assert.Equal(DirectCast(42380, ERRID), ERRID.WRN_Experimental)
+
+ Dim cmd = New MockVisualBasicCompiler(Nothing, dir.Path, {
+ "/nologo",
+ "/t:library",
+ "/preferreduilang:en",
+ "/analyzerconfig:" + analyzerConfig.Path,
+ src.Path})
+
+ Dim outWriter = New StringWriter(CultureInfo.InvariantCulture)
+ Dim exitCode = cmd.Run(outWriter)
+ Assert.Equal(0, exitCode)
+ ' Note: the behavior differs from C# in that the editorconfig rule is applied
+ Assert.Contains("warning DiagID: 'C' is for evaluation purposes only and is subject to change or removal in future updates.", outWriter.ToString())
+ End Sub
+
Private Function EmitGenerator(ByVal targetFramework As String) As String
Dim targetFrameworkAttributeText As String = If(TypeOf targetFramework Is Object, $"", String.Empty)
Dim generatorSource As String = $"
diff --git a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb
index 62999f5447831..e662cc8b0c1e1 100644
--- a/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb
+++ b/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb
@@ -5465,6 +5465,304 @@ DiagID1: 'Derived' is for evaluation purposes only and is subject to change or r
Assert.Equal(ObsoleteAttributeKind.Experimental, derivedType.ContainingModule.ObsoleteKind)
End Sub
+
+ Public Sub NullDiagnosticId()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+Class C
+End Class
+
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+
+ comp.AssertTheseDiagnostics(
+
+ ~~~~~~~
+BC42380: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ Sub M(c As C)
+ ~
+]]>)
+ End Sub
+
+
+ Public Sub MissingDiagnosticIdArgument()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+Class C
+End Class
+
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+
+ comp.AssertTheseDiagnostics(
+
+ ~~~~~~~~~~~~
+]]>)
+ End Sub
+
+
+ Public Sub IntegerDiagnosticIdArgument()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+Class C
+End Class
+
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+
+ comp.AssertTheseDiagnostics(
+
+ ~~
+]]>)
+ End Sub
+
+
+ Public Sub MultipleArguments()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+Class C
+End Class
+
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+
+ comp.AssertTheseDiagnostics(
+
+ ~~~~~~~
+]]>)
+ End Sub
+
+
+ Public Sub WhitespaceDiagnosticId()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+Class C
+End Class
+
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+
+ comp.AssertTheseDiagnostics(
+
+ ~~~
+BC42380: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ Sub M(c As C)
+ ~
+]]>)
+ End Sub
+
+
+ Public Sub WhitespaceDiagnosticId_WithSuppression()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+Class C
+End Class
+
+#Disable Warning BC42380
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+
+ Assert.Equal(DirectCast(42380, ERRID), ERRID.WRN_Experimental)
+
+ comp.AssertTheseDiagnostics(
+
+ ~~~
+]]>)
+ End Sub
+
+
+ Public Sub SpacedDiagnosticId()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+Class C
+End Class
+
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+
+ comp.AssertTheseDiagnostics(
+
+ ~~~~~~~~~
+Diag 01: 'C' is for evaluation purposes only and is subject to change or removal in future updates.
+ Sub M(c As C)
+ ~
+]]>)
+ End Sub
+
+
+ Public Sub SpacedDiagnosticId_WithSecondArgument()
+ Dim attrComp = CreateCSharpCompilation(experimentalAttributeCSharpSrc)
+
+ Dim src =
+
+
+Class C
+End Class
+
+Class D
+ Sub M(c As C)
+ End Sub
+End Class
+]]>
+
+
+
+ Dim comp = CreateCompilation(src, references:={attrComp.EmitToImageReference()})
+
+ comp.AssertTheseDiagnostics(
+
+ ~~~~~~~
+]]>)
+ End Sub
+
+
+ Public Sub SpacedDiagnosticId_Metadata()
+ Dim il =
+
+ Dim src =
+
+
+
+
+
+ Dim comp = CreateCompilationWithCustomILSource(src, ilSource:=il)
+
+ comp.AssertTheseDiagnostics(
+)
+ End Sub
+
Public Sub MissingAssemblyAndModule()