提交 b0f5a5fa 编写于 作者: N Neal Gafter

Merge remote-tracking branch 'upstream/master' into merges/master-to-features/target-typing

# /warnversion warning "waves"
The C# compiler flag `/warnversion` controls optional warnings.
When we introduce new warnings that can be reported on existing code,
we do so under an opt-in system so that programmers do not see new warnings
without taking action to enable them.
For that purpose, we have introduced the compiler flag "`/warnversion=n`"
where `n` is a whole number or a decimal number.
For a warning that was introduced in dotnet version `k`,
that warning will be produced if the warning version `n` specified when compiling is
greater than or equal to `k` and a compiler shipped with dotnet version
`k` or later is used to compile the code.
The default warning version is `0` (produce no optional warnings).
Our first warning under control of `/warnversion` was introduced in version `5`
as part of .NET 5.
If you want the compiler to produce all applicable warnings, you can specify
`/warnversion=9999`.
In the project file, the property used to specify the warning version is `AnalysisLevel`.
The table below describes all of the warnings controlled by `/warnversion`.
| Warning ID | warnversion | Description |
|------------|---------|-------------|
| CS8073 | 5 | [Expression always true (or false) when comparing a struct to null](https://github.com/dotnet/roslyn/issues/45744) |
......@@ -190,9 +190,12 @@ protected sealed override void InitializeWorker(AnalysisContext context)
// Remove entries for unhandled diagnostic ids.
foreach (var id in unhandledIds)
{
foreach (var (pragma, _) in idToPragmasMap[id])
if (idToPragmasMap.TryGetValue(id, out var pragmas))
{
pragmasToIsUsedMap.Remove(pragma);
foreach (var (pragma, _) in pragmas)
{
pragmasToIsUsedMap.Remove(pragma);
}
}
if (idToSuppressMessageAttributesMap.TryGetValue(id, out var attributeNodes))
......
......@@ -42,6 +42,17 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
/// </summary>
public override NullableContextOptions NullableContextOptions { get; protected set; }
/// <summary>
/// The warning version, which indicates which set of warnings should be produced by the compiler.
/// </summary>
/// <remarks>
/// Most warnings are produced by the compiler unconditionally, but warnings that are controlled by a version
/// are only produced when the WarningVersion value is greater than or equal to the warning version
/// associated with the warning. Use zero to get none of the versioned warnings, or a large number like 9999
/// to get all of the versioned warnings. The first version that enables some warnings is version 5.
/// </remarks>
public decimal WarningVersion { get; private set; }
// Defaults correspond to the compiler's defaults or indicate that the user did not specify when that is significant.
// That's significant when one option depends on another's setting. SubsystemVersion depends on Platform and Target.
public CSharpCompilationOptions(
......@@ -71,7 +82,58 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
StrongNameProvider? strongNameProvider = null,
bool publicSign = false,
MetadataImportOptions metadataImportOptions = MetadataImportOptions.Public,
NullableContextOptions nullableContextOptions = NullableContextOptions.Disable)
NullableContextOptions nullableContextOptions = NullableContextOptions.Disable,
decimal warningVersion = 0m)
: this(outputKind, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName,
usings, optimizationLevel, checkOverflow, allowUnsafe,
cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, platform,
generalDiagnosticOption, warningLevel,
specificDiagnosticOptions, concurrentBuild, deterministic,
currentLocalTime: default,
debugPlusMode: false,
xmlReferenceResolver: xmlReferenceResolver,
sourceReferenceResolver: sourceReferenceResolver,
metadataReferenceResolver: metadataReferenceResolver,
assemblyIdentityComparer: assemblyIdentityComparer,
strongNameProvider: strongNameProvider,
metadataImportOptions: metadataImportOptions,
referencesSupersedeLowerVersions: false,
publicSign: publicSign,
topLevelBinderFlags: BinderFlags.None,
nullableContextOptions: nullableContextOptions,
warningVersion: warningVersion)
{
}
// 16.8 BACKCOMPAT OVERLOAD -- DO NOT TOUCH
public CSharpCompilationOptions(
OutputKind outputKind,
bool reportSuppressedDiagnostics,
string? moduleName,
string? mainTypeName,
string? scriptClassName,
IEnumerable<string>? usings,
OptimizationLevel optimizationLevel,
bool checkOverflow,
bool allowUnsafe,
string? cryptoKeyContainer,
string? cryptoKeyFile,
ImmutableArray<byte> cryptoPublicKey,
bool? delaySign,
Platform platform,
ReportDiagnostic generalDiagnosticOption,
int warningLevel,
IEnumerable<KeyValuePair<string, ReportDiagnostic>>? specificDiagnosticOptions,
bool concurrentBuild,
bool deterministic,
XmlReferenceResolver? xmlReferenceResolver,
SourceReferenceResolver? sourceReferenceResolver,
MetadataReferenceResolver? metadataReferenceResolver,
AssemblyIdentityComparer? assemblyIdentityComparer,
StrongNameProvider? strongNameProvider,
bool publicSign,
MetadataImportOptions metadataImportOptions,
NullableContextOptions nullableContextOptions)
: this(outputKind, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName,
usings, optimizationLevel, checkOverflow, allowUnsafe,
cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, platform,
......@@ -211,7 +273,8 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
bool referencesSupersedeLowerVersions,
bool publicSign,
BinderFlags topLevelBinderFlags,
NullableContextOptions nullableContextOptions)
NullableContextOptions nullableContextOptions,
decimal warningVersion = 0m)
: base(outputKind, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName,
cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, publicSign, optimizationLevel, checkOverflow,
platform, generalDiagnosticOption, warningLevel, specificDiagnosticOptions.ToImmutableDictionaryOrEmpty(),
......@@ -223,6 +286,7 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
this.AllowUnsafe = allowUnsafe;
this.TopLevelBinderFlags = topLevelBinderFlags;
this.NullableContextOptions = nullableContextOptions;
this.WarningVersion = warningVersion;
}
private CSharpCompilationOptions(CSharpCompilationOptions other) : this(
......@@ -256,7 +320,8 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
reportSuppressedDiagnostics: other.ReportSuppressedDiagnostics,
publicSign: other.PublicSign,
topLevelBinderFlags: other.TopLevelBinderFlags,
nullableContextOptions: other.NullableContextOptions)
nullableContextOptions: other.NullableContextOptions,
warningVersion: other.WarningVersion)
{
}
......@@ -493,6 +558,16 @@ public CSharpCompilationOptions WithWarningLevel(int warningLevel)
return new CSharpCompilationOptions(this) { WarningLevel = warningLevel };
}
public CSharpCompilationOptions WithWarningVersion(decimal warningVersion)
{
if (warningVersion == this.WarningVersion)
{
return this;
}
return new CSharpCompilationOptions(this) { WarningVersion = warningVersion };
}
public new CSharpCompilationOptions WithConcurrentBuild(bool concurrentBuild)
{
if (concurrentBuild == this.ConcurrentBuild)
......@@ -688,6 +763,11 @@ internal override void ValidateOptions(ArrayBuilder<Diagnostic> builder)
builder.Add(Diagnostic.Create(MessageProvider.Instance, (int)ErrorCode.ERR_BadCompilationOptionValue, nameof(WarningLevel), WarningLevel));
}
if (WarningVersion < 0m)
{
builder.Add(Diagnostic.Create(MessageProvider.Instance, (int)ErrorCode.ERR_BadCompilationOptionValue, nameof(WarningVersion), WarningVersion));
}
if (Usings != null && Usings.Any(u => !u.IsValidClrNamespaceName()))
{
builder.Add(Diagnostic.Create(MessageProvider.Instance, (int)ErrorCode.ERR_BadCompilationOptionValue, nameof(Usings), Usings.Where(u => !u.IsValidClrNamespaceName()).First() ?? "null"));
......@@ -722,8 +802,9 @@ public bool Equals(CSharpCompilationOptions? other)
return this.AllowUnsafe == other.AllowUnsafe &&
this.TopLevelBinderFlags == other.TopLevelBinderFlags &&
(this.Usings == null ? other.Usings == null : this.Usings.SequenceEqual(other.Usings, StringComparer.Ordinal) &&
this.NullableContextOptions == other.NullableContextOptions);
(this.Usings == null ? other.Usings == null : this.Usings.SequenceEqual(other.Usings, StringComparer.Ordinal)) &&
this.NullableContextOptions == other.NullableContextOptions &&
this.WarningVersion == other.WarningVersion;
}
public override bool Equals(object? obj)
......@@ -736,7 +817,8 @@ public override int GetHashCode()
return Hash.Combine(base.GetHashCodeHelper(),
Hash.Combine(this.AllowUnsafe,
Hash.Combine(Hash.CombineValues(this.Usings, StringComparer.Ordinal),
Hash.Combine(TopLevelBinderFlags.GetHashCode(), this.NullableContextOptions.GetHashCode()))));
Hash.Combine(this.WarningVersion.GetHashCode(),
Hash.Combine(TopLevelBinderFlags.GetHashCode(), this.NullableContextOptions.GetHashCode())))));
}
internal override Diagnostic FilterDiagnostic(Diagnostic diagnostic)
......
......@@ -3235,6 +3235,9 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep
<data name="ERR_BadWarningLevel" xml:space="preserve">
<value>Warning level must be in the range 0-4</value>
</data>
<data name="ERR_BadWarningVersion" xml:space="preserve">
<value>Warning version must be greater than or equal to '0'.</value>
</data>
<data name="ERR_BadDebugType" xml:space="preserve">
<value>Invalid option '{0}' for /debug; must be 'portable', 'embedded', 'full' or 'pdbonly'</value>
</data>
......@@ -4617,7 +4620,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -135,6 +135,7 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string? ba
bool publicSign = false;
string? sourceLink = null;
string? ruleSetPath = null;
decimal warningVersion = 0m;
// Process ruleset files first so that diagnostic severity settings specified on the command line via
// /nowarn and /warnaserror can override diagnostic severity settings specified in the ruleset file.
......@@ -905,6 +906,24 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string? ba
}
continue;
case "warnversion":
value = RemoveQuotesAndSlashes(value);
decimal newWarningVersion;
if (string.IsNullOrEmpty(value) ||
!decimal.TryParse(value, NumberStyles.Integer | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out newWarningVersion))
{
AddDiagnostic(diagnostics, ErrorCode.ERR_SwitchNeedsNumber, name);
}
else if (newWarningVersion < 0m)
{
AddDiagnostic(diagnostics, ErrorCode.ERR_BadWarningVersion, name);
}
else
{
warningVersion = newWarningVersion;
}
continue;
case "nowarn":
if (value == null)
{
......@@ -1419,7 +1438,8 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string? ba
warningLevel: warningLevel,
specificDiagnosticOptions: diagnosticOptions,
reportSuppressedDiagnostics: reportSuppressedDiagnostics,
publicSign: publicSign
publicSign: publicSign,
warningVersion: warningVersion
);
if (debugPlus)
......
......@@ -1835,6 +1835,7 @@ internal enum ErrorCode
ERR_StaticAnonymousFunctionCannotCaptureVariable = 8820,
ERR_StaticAnonymousFunctionCannotCaptureThis = 8821,
ERR_BadWarningVersion = 8848,
ERR_ExpressionTreeContainsWithExpression = 8849,
ERR_BadRecordDeclaration = 8850,
ERR_DuplicateRecordConstructor = 8851,
......
......@@ -8,6 +8,7 @@
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
......@@ -206,6 +207,35 @@ private static System.Resources.ResourceManager ResourceManager
}
}
/// <summary>
/// For each warning that is linked to a warning "wave" (/warnversion), the number of
/// the warning wave in which it was introduced.
/// </summary>
internal static decimal GetWarningVersion(this ErrorCode code)
{
// *** ATTENTION! If you are modifying this method, there are things you need to do.
// If you introduce a new warning "wave" version (i.e. a new return value
// possible from this method), please update
// CSharpResources.resx to include documentation for the "latest"
// warning version in "IDS_CSCHelp" under "-warnversion". Note that
// it is our intention that warning wave version numbers correspond
// to the dotnet platform with which the compiler is released.
// If you introduce a new warning under control of the /warnversion flag
// (i.e. a new case in the following switch statement), please update
// the file "docs\compilers\CSharp\Warnversion Warning Waves.md"
// to document it.
switch (code)
{
case ErrorCode.WRN_NubExprIsConstBool2:
return 5m;
default:
throw ExceptionUtilities.UnexpectedValue(code);
}
}
internal static int GetWarningLevel(ErrorCode code)
{
if (IsInfo(code) || IsHidden(code))
......
......@@ -792,7 +792,7 @@ private void CheckLiftedBinOp(BoundBinaryOperator node)
string always = node.OperatorKind.Operator() == BinaryOperatorKind.NotEqual ? "true" : "false";
if (_compilation.FeatureStrictEnabled || !node.OperatorKind.IsUserDefined())
if (_compilation.FeatureStrictEnabled || !node.OperatorKind.IsUserDefined() || ErrorCode.WRN_NubExprIsConstBool2.GetWarningVersion() <= this._compilation.Options.WarningVersion)
{
if (node.Right.NullableNeverHasValue() && node.Left.NullableAlwaysHasValue())
{
......
*REMOVED*Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind outputKind, bool reportSuppressedDiagnostics = false, string moduleName = null, string mainTypeName = null, string scriptClassName = null, System.Collections.Generic.IEnumerable<string> usings = null, Microsoft.CodeAnalysis.OptimizationLevel optimizationLevel = Microsoft.CodeAnalysis.OptimizationLevel.Debug, bool checkOverflow = false, bool allowUnsafe = false, string cryptoKeyContainer = null, string cryptoKeyFile = null, System.Collections.Immutable.ImmutableArray<byte> cryptoPublicKey = default(System.Collections.Immutable.ImmutableArray<byte>), bool? delaySign = null, Microsoft.CodeAnalysis.Platform platform = Microsoft.CodeAnalysis.Platform.AnyCpu, Microsoft.CodeAnalysis.ReportDiagnostic generalDiagnosticOption = Microsoft.CodeAnalysis.ReportDiagnostic.Default, int warningLevel = 4, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, Microsoft.CodeAnalysis.ReportDiagnostic>> specificDiagnosticOptions = null, bool concurrentBuild = true, bool deterministic = false, Microsoft.CodeAnalysis.XmlReferenceResolver xmlReferenceResolver = null, Microsoft.CodeAnalysis.SourceReferenceResolver sourceReferenceResolver = null, Microsoft.CodeAnalysis.MetadataReferenceResolver metadataReferenceResolver = null, Microsoft.CodeAnalysis.AssemblyIdentityComparer assemblyIdentityComparer = null, Microsoft.CodeAnalysis.StrongNameProvider strongNameProvider = null, bool publicSign = false, Microsoft.CodeAnalysis.MetadataImportOptions metadataImportOptions = Microsoft.CodeAnalysis.MetadataImportOptions.Public, Microsoft.CodeAnalysis.NullableContextOptions nullableContextOptions = Microsoft.CodeAnalysis.NullableContextOptions.Disable) -> void
Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind outputKind, bool reportSuppressedDiagnostics = false, string moduleName = null, string mainTypeName = null, string scriptClassName = null, System.Collections.Generic.IEnumerable<string> usings = null, Microsoft.CodeAnalysis.OptimizationLevel optimizationLevel = Microsoft.CodeAnalysis.OptimizationLevel.Debug, bool checkOverflow = false, bool allowUnsafe = false, string cryptoKeyContainer = null, string cryptoKeyFile = null, System.Collections.Immutable.ImmutableArray<byte> cryptoPublicKey = default(System.Collections.Immutable.ImmutableArray<byte>), bool? delaySign = null, Microsoft.CodeAnalysis.Platform platform = Microsoft.CodeAnalysis.Platform.AnyCpu, Microsoft.CodeAnalysis.ReportDiagnostic generalDiagnosticOption = Microsoft.CodeAnalysis.ReportDiagnostic.Default, int warningLevel = 4, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, Microsoft.CodeAnalysis.ReportDiagnostic>> specificDiagnosticOptions = null, bool concurrentBuild = true, bool deterministic = false, Microsoft.CodeAnalysis.XmlReferenceResolver xmlReferenceResolver = null, Microsoft.CodeAnalysis.SourceReferenceResolver sourceReferenceResolver = null, Microsoft.CodeAnalysis.MetadataReferenceResolver metadataReferenceResolver = null, Microsoft.CodeAnalysis.AssemblyIdentityComparer assemblyIdentityComparer = null, Microsoft.CodeAnalysis.StrongNameProvider strongNameProvider = null, bool publicSign = false, Microsoft.CodeAnalysis.MetadataImportOptions metadataImportOptions = Microsoft.CodeAnalysis.MetadataImportOptions.Public, Microsoft.CodeAnalysis.NullableContextOptions nullableContextOptions = Microsoft.CodeAnalysis.NullableContextOptions.Disable, decimal warningVersion = 0) -> void
Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind outputKind, bool reportSuppressedDiagnostics, string moduleName, string mainTypeName, string scriptClassName, System.Collections.Generic.IEnumerable<string> usings, Microsoft.CodeAnalysis.OptimizationLevel optimizationLevel, bool checkOverflow, bool allowUnsafe, string cryptoKeyContainer, string cryptoKeyFile, System.Collections.Immutable.ImmutableArray<byte> cryptoPublicKey, bool? delaySign, Microsoft.CodeAnalysis.Platform platform, Microsoft.CodeAnalysis.ReportDiagnostic generalDiagnosticOption, int warningLevel, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, Microsoft.CodeAnalysis.ReportDiagnostic>> specificDiagnosticOptions, bool concurrentBuild, bool deterministic, Microsoft.CodeAnalysis.XmlReferenceResolver xmlReferenceResolver, Microsoft.CodeAnalysis.SourceReferenceResolver sourceReferenceResolver, Microsoft.CodeAnalysis.MetadataReferenceResolver metadataReferenceResolver, Microsoft.CodeAnalysis.AssemblyIdentityComparer assemblyIdentityComparer, Microsoft.CodeAnalysis.StrongNameProvider strongNameProvider, bool publicSign, Microsoft.CodeAnalysis.MetadataImportOptions metadataImportOptions, Microsoft.CodeAnalysis.NullableContextOptions nullableContextOptions) -> void
Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.WarningVersion.get -> decimal
Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions.WithWarningVersion(decimal warningVersion) -> Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions
Microsoft.CodeAnalysis.CSharp.Conversion.IsConditionalExpression.get -> bool
Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax.AddModifiers(params Microsoft.CodeAnalysis.SyntaxToken[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList modifiers) -> Microsoft.CodeAnalysis.CSharp.Syntax.AnonymousFunctionExpressionSyntax
......
......@@ -152,6 +152,11 @@
<target state="translated">Chyba syntaxe příkazového řádku: {0} není platná hodnota možnosti {1}. Hodnota musí mít tvar {2}.</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">Fehler in der Befehlszeilensyntax: "{0}" ist kein gültiger Wert für die Option "{1}". Der Wert muss im Format "{2}" vorliegen.</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">Error de sintaxis de la línea de comandos: "{0}" no es un valor válido para la opción "{1}". El valor debe tener el formato "{2}".</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">Erreur de syntaxe de ligne de commande : '{0}' est une valeur non valide pour l'option '{1}'. La valeur doit se présenter sous la forme '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">Errore di sintassi della riga di comando: '{0}' non è un valore valido per l'opzione '{1}'. Il valore deve essere espresso nel formato '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">コマンドライン構文エラー: '{0}' は、'{1}' オプションの有効な値ではありません。値は '{2}' の形式にする必要があります。</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">명령줄 구문 오류: '{0}'은(는) '{1}' 옵션에 유효한 값이 아닙니다. 값은 '{2}' 형식이어야 합니다.</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">Błąd składni wiersza polecenia: „{0}” nie jest prawidłową wartością dla opcji „{1}”. Wartość musi mieć postać „{2}”.</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">Erro de sintaxe de linha de comando: '{0}' não é um valor válido para a opção '{1}'. O valor precisa estar no formato '{2}'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">Ошибка в синтаксисе командной строки: "{0}" не является допустимым значением для параметра "{1}". Значение должно иметь форму "{2}".</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">Komut satırı söz dizimi hatası: '{0}', '{1}' seçeneği için geçerli bir değer değil. Değer '{2}' biçiminde olmalıdır.</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">命令行语法错误:“{0}”不是“{1}”选项的有效值。值的格式必须为 "{2}"。</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -152,6 +152,11 @@
<target state="translated">命令列語法錯誤: '{0}' 對 '{1}' 選項而言不是有效的值。此值的格式必須是 '{2}'。</target>
<note />
</trans-unit>
<trans-unit id="ERR_BadWarningVersion">
<source>Warning version must be greater than or equal to '0'.</source>
<target state="new">Warning version must be greater than or equal to '0'.</target>
<note />
</trans-unit>
<trans-unit id="ERR_CannotConvertAddressOfToDelegate">
<source>Cannot convert &amp;method group '{0}' to delegate type '{0}'.</source>
<target state="new">Cannot convert &amp;method group '{0}' to delegate type '{0}'.</target>
......@@ -954,7 +959,9 @@
-warnaserror[+|-] Report all warnings as errors
-warnaserror[+|-]:&lt;warn list&gt; Report specific warnings as errors
(use "nullable" for all nullability warnings)
-warn:&lt;n&gt; Set warning level (0-4) (Short form: -w)
-warnversion:&lt;n&gt; Enable recently introduced warnings. Default '0'.
To enable warnings introduced through this version, use '5'.
Use '9999' to enable all current and future warning waves.
-nowarn:&lt;warn list&gt; Disable specific warning messages
(use "nullable" for all nullability warnings)
-ruleset:&lt;file&gt; Specify a ruleset file that disables specific
......
......@@ -5210,6 +5210,49 @@ public void WarningsErrors()
parsedArgs.Errors.Verify();
}
[Fact]
public void WarningVersion()
{
var parsedArgs = DefaultParse(new string[] { "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(0m, parsedArgs.CompilationOptions.WarningVersion);
parsedArgs = DefaultParse(new string[] { "/warnversion", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify(
Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("warnversion").WithLocation(1, 1)
);
Assert.Equal(0m, parsedArgs.CompilationOptions.WarningVersion);
parsedArgs = DefaultParse(new string[] { "/warnversion:", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify(
Diagnostic(ErrorCode.ERR_SwitchNeedsNumber).WithArguments("warnversion").WithLocation(1, 1)
);
Assert.Equal(0m, parsedArgs.CompilationOptions.WarningVersion);
parsedArgs = DefaultParse(new string[] { "/warnversion:-1", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify(
// Warning version must be greater than or equal to '0'.
Diagnostic(ErrorCode.ERR_BadWarningVersion).WithArguments("warnversion").WithLocation(1, 1)
);
Assert.Equal(0m, parsedArgs.CompilationOptions.WarningVersion);
parsedArgs = DefaultParse(new string[] { "/warnversion:0", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(0m, parsedArgs.CompilationOptions.WarningVersion);
parsedArgs = DefaultParse(new string[] { "/warnversion:5", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(5m, parsedArgs.CompilationOptions.WarningVersion);
parsedArgs = DefaultParse(new string[] { "/warnversion:5.1", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(5.1m, parsedArgs.CompilationOptions.WarningVersion);
parsedArgs = DefaultParse(new string[] { "/warnversion:9999", "a.cs" }, WorkingDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(9999m, parsedArgs.CompilationOptions.WarningVersion);
}
private static void AssertSpecificDiagnostics(int[] expectedCodes, ReportDiagnostic[] expectedOptions, CSharpCommandLineArguments args)
{
var actualOrdered = args.CompilationOptions.SpecificDiagnosticOptions.OrderBy(entry => entry.Key);
......@@ -12618,6 +12661,61 @@ public void TestAdditionalFileAnalyzer(bool registerFromInitialize)
CleanupAllGeneratedFiles(srcDirectory.Path);
}
[Fact, WorkItem(45702, "https://github.com/dotnet/roslyn/issues/45702")]
public void TestWarningVersion()
{
string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@"
class Program
{
public static void Main() { }
public static void M(S s)
{
if (s == null) { }
}
}
struct S
{
public static bool operator==(S s1, S s2) => false;
public static bool operator!=(S s1, S s2) => true;
public override bool Equals(object other) => false;
public override int GetHashCode() => 0;
}
").Path;
var baseDir = Path.GetDirectoryName(source);
var fileName = Path.GetFileName(source);
decimal requiredVersion = ErrorCode.WRN_NubExprIsConstBool2.GetWarningVersion();
Assert.Equal(5m, requiredVersion);
foreach (decimal level in new[] { -1m, 0m, 4m, 4.9m, 5m, 5.0m, 5.1m, 9999m })
{
var outWriter = new StringWriter(CultureInfo.InvariantCulture);
int exitCode = CreateCSharpCompiler(null, baseDir, new[] { "/nologo", "/preferreduilang:en", FormattableString.Invariant($"/warnversion:{level}"), source.ToString() }).Run(outWriter);
if (level < 0)
{
Assert.Equal(1, exitCode);
Assert.Equal(
"error CS8848: Warning version must be greater than or equal to '0'.",
outWriter.ToString().Trim());
}
else if (level >= requiredVersion)
{
Assert.Equal(0, exitCode);
Assert.Equal(
$@"{fileName}(7,13): warning CS8073: The result of the expression is always 'false' since a value of type 'S' is never equal to 'null' of type 'S?'",
outWriter.ToString().Trim());
}
else
{
Assert.Equal(0, exitCode);
Assert.Equal("", outWriter.ToString().Trim());
}
CleanupAllGeneratedFiles(source);
}
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
/// <summary>
/// Tests that exercise warnings that are under control of the compiler option <see cref="CSharpCompilationOptions.WarningVersion"/>.
/// </summary>
public class WarningVersionTests : CompilingTestBase
{
[Fact]
public void CompareStructToNull()
{
var source = @"
class Program
{
public static void M(S s)
{
if (s == null) { }
if (s != null) { }
}
}
struct S
{
public static bool operator==(S s1, S s2) => false;
public static bool operator!=(S s1, S s2) => true;
public override bool Equals(object other) => false;
public override int GetHashCode() => 0;
}
";
var whenWave5 = new[]
{
// (6,13): warning CS8073: The result of the expression is always 'false' since a value of type 'S' is never equal to 'null' of type 'S?'
// if (s == null) { }
Diagnostic(ErrorCode.WRN_NubExprIsConstBool2, "s == null").WithArguments("false", "S", "S?").WithLocation(6, 13),
// (7,13): warning CS8073: The result of the expression is always 'true' since a value of type 'S' is never equal to 'null' of type 'S?'
// if (s != null) { }
Diagnostic(ErrorCode.WRN_NubExprIsConstBool2, "s != null").WithArguments("true", "S", "S?").WithLocation(7, 13)
};
CreateCompilation(source).VerifyDiagnostics();
CreateCompilation(source, options: TestOptions.ReleaseDll.WithWarningVersion(4.9m)).VerifyDiagnostics();
CreateCompilation(source, options: TestOptions.ReleaseDll.WithWarningVersion(5m)).VerifyDiagnostics(whenWave5);
CreateCompilation(source, options: TestOptions.ReleaseDll.WithWarningVersion(5.0m)).VerifyDiagnostics(whenWave5);
CreateCompilation(source, options: TestOptions.ReleaseDll.WithWarningVersion(5.1m)).VerifyDiagnostics(whenWave5);
}
}
}
......@@ -367,6 +367,7 @@ public void TestFieldsForEqualsAndGetHashCode()
"Language",
"AllowUnsafe",
"Usings",
"WarningVersion",
"TopLevelBinderFlags",
"NullableContextOptions");
}
......
......@@ -265,23 +265,34 @@ void M()
}
}
public enum TestKind
{
Pragmas,
SuppressMessageAttributes,
PragmasAndSuppressMessageAttributes
}
[Theory, CombinatorialData]
public async Task TestDoNotRemoveUnsupportedDiagnosticSuppression(bool disable)
[WorkItem(46047, "https://github.com/dotnet/roslyn/issues/46047")]
public async Task TestDoNotRemoveUnsupportedDiagnosticSuppression(bool disable, TestKind testKind)
{
var disableOrRestore = disable ? "disable" : "restore";
var pragmas = new StringBuilder();
var suppressMessageAttribtes = new StringBuilder();
foreach (var id in UnsupportedDiagnosticIds)
{
pragmas.AppendLine($@"#pragma warning {disableOrRestore} {id}");
suppressMessageAttribtes.AppendLine($@"[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""{id}"")]");
if (testKind == TestKind.Pragmas || testKind == TestKind.PragmasAndSuppressMessageAttributes)
pragmas.AppendLine($@"#pragma warning {disableOrRestore} {id}");
if (testKind == TestKind.SuppressMessageAttributes || testKind == TestKind.PragmasAndSuppressMessageAttributes)
suppressMessageAttribtes.AppendLine($@"[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""{id}"")]");
}
var source = $@"{{|FixAllInDocument:{pragmas}{suppressMessageAttribtes}|}}class Class {{ }}";
// Compiler diagnostics cannot be suppressed with SuppressMessageAttribute.
// Hence, attribute suppressions for compiler diagnostics are always unnecessary.
if (!IsCompilerDiagnosticsTest)
if (!IsCompilerDiagnosticsTest || testKind == TestKind.Pragmas)
{
await TestMissingInRegularAndScriptAsync(source);
}
......
......@@ -945,4 +945,10 @@ Do you want to proceed?</value>
<data name="Downloading_IntelliSense_index_for_0" xml:space="preserve">
<value>Downloading IntelliSense index for {0}</value>
</data>
<data name="Error_creating_instance_of_CodeFixProvider_0" xml:space="preserve">
<value>Error creating instance of CodeFixProvider '{0}'</value>
</data>
<data name="Error_creating_instance_of_CodeFixProvider" xml:space="preserve">
<value>Error creating instance of CodeFixProvider</value>
</data>
</root>
\ No newline at end of file
......@@ -42,13 +42,15 @@ internal partial class CodeFixService : ForegroundThreadAffinitizedObject, ICode
private readonly IDiagnosticAnalyzerService _diagnosticService;
private readonly ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<DiagnosticId, ImmutableArray<CodeFixProvider>>>> _workspaceFixersMap;
private readonly Func<Workspace, ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<DiagnosticId, ImmutableArray<CodeFixProvider>>>>> _getWorkspaceFixersMap;
private ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<DiagnosticId, ImmutableArray<CodeFixProvider>>>>? _lazyWorkspaceFixersMap;
private readonly ConditionalWeakTable<IReadOnlyList<AnalyzerReference>, ImmutableDictionary<DiagnosticId, List<CodeFixProvider>>> _projectFixersMap;
// Shared by project fixers and workspace fixers.
private ImmutableDictionary<CodeFixProvider, ImmutableArray<DiagnosticId>> _fixerToFixableIdsMap = ImmutableDictionary<CodeFixProvider, ImmutableArray<DiagnosticId>>.Empty;
private readonly ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<CodeFixProvider, int>>> _fixerPriorityMap;
private readonly Func<Workspace, ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<CodeFixProvider, int>>>> _getFixerPriorityMap;
private ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<CodeFixProvider, int>>>? _lazyFixerPriorityMap;
private readonly ConditionalWeakTable<AnalyzerReference, ProjectCodeFixProvider> _analyzerReferenceToFixersMap;
private readonly ConditionalWeakTable<AnalyzerReference, ProjectCodeFixProvider>.CreateValueCallback _createProjectCodeFixProvider;
......@@ -62,22 +64,22 @@ internal partial class CodeFixService : ForegroundThreadAffinitizedObject, ICode
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
public CodeFixService(
IThreadingContext threadingContext,
IDiagnosticAnalyzerService service,
IDiagnosticAnalyzerService diagnosticAnalyzerService,
[ImportMany] IEnumerable<Lazy<IErrorLoggerService>> loggers,
[ImportMany] IEnumerable<Lazy<CodeFixProvider, CodeChangeProviderMetadata>> fixers,
[ImportMany] IEnumerable<Lazy<IConfigurationFixProvider, CodeChangeProviderMetadata>> configurationProviders)
: base(threadingContext, assertIsForeground: false)
{
_errorLoggers = loggers;
_diagnosticService = service;
_diagnosticService = diagnosticAnalyzerService;
var fixersPerLanguageMap = fixers.ToPerLanguageMapWithMultipleLanguages();
var configurationProvidersPerLanguageMap = configurationProviders.ToPerLanguageMapWithMultipleLanguages();
_workspaceFixersMap = GetFixerPerLanguageMap(fixersPerLanguageMap, null);
_getWorkspaceFixersMap = workspace => GetFixerPerLanguageMap(fixersPerLanguageMap, workspace);
_configurationProvidersMap = GetConfigurationProvidersPerLanguageMap(configurationProvidersPerLanguageMap);
// REVIEW: currently, fixer's priority is statically defined by the fixer itself. might considering making it more dynamic or configurable.
_fixerPriorityMap = GetFixerPriorityPerLanguageMap(fixersPerLanguageMap);
_getFixerPriorityMap = workspace => GetFixerPriorityPerLanguageMap(fixersPerLanguageMap, workspace);
// Per-project fixers
_projectFixersMap = new ConditionalWeakTable<IReadOnlyList<AnalyzerReference>, ImmutableDictionary<string, List<CodeFixProvider>>>();
......@@ -202,7 +204,7 @@ public async Task<ImmutableArray<CodeFixCollection>> GetFixesAsync(Document docu
result, addOperationScope, cancellationToken).ConfigureAwait(false);
}
if (result.Count > 0 && _fixerPriorityMap.TryGetValue(document.Project.Language, out var fixersForLanguage))
if (result.Count > 0 && TryGetWorkspaceFixersPriorityMap(document, out var fixersForLanguage))
{
// sort the result to the order defined by the fixers
#pragma warning disable IDE0007 // Use implicit type - Explicit type is need to suppress an incorrect nullable warning on dereferencing the map.
......@@ -266,6 +268,68 @@ public async Task<Document> ApplyCodeFixesForSpecificDiagnosticIdAsync(Document
return solution.GetDocument(document.Id) ?? throw new NotSupportedException(EditorFeaturesResources.Removal_of_document_not_supported);
}
private bool TryGetWorkspaceFixersMap(Document document, out Lazy<ImmutableDictionary<DiagnosticId, ImmutableArray<CodeFixProvider>>> fixerMap)
{
if (_lazyWorkspaceFixersMap == null)
{
var workspaceFixersMap = _getWorkspaceFixersMap(document.Project.Solution.Workspace);
Interlocked.CompareExchange(ref _lazyWorkspaceFixersMap, workspaceFixersMap, null);
}
return _lazyWorkspaceFixersMap.TryGetValue(document.Project.Language, out fixerMap);
}
private bool TryGetWorkspaceFixersPriorityMap(Document document, out Lazy<ImmutableDictionary<CodeFixProvider, int>> fixersPriorityMap)
{
if (_lazyFixerPriorityMap == null)
{
var fixersPriorityByLanguageMap = _getFixerPriorityMap(document.Project.Solution.Workspace);
Interlocked.CompareExchange(ref _lazyFixerPriorityMap, fixersPriorityByLanguageMap, null);
}
return _lazyFixerPriorityMap.TryGetValue(document.Project.Language, out fixersPriorityMap);
}
private bool TryGetWorkspaceFixer(
Lazy<CodeFixProvider, CodeChangeProviderMetadata> lazyFixer,
Workspace workspace,
bool logExceptionWithInfoBar,
[NotNullWhen(returnValue: true)] out CodeFixProvider? fixer)
{
try
{
fixer = lazyFixer.Value;
return true;
}
catch (Exception ex)
{
// Gracefully handle exceptions in creating fixer instance.
// Log exception and show info bar, if needed.
if (logExceptionWithInfoBar)
{
var errorReportingService = workspace.Services.GetRequiredService<IErrorReportingService>();
var message = lazyFixer.Metadata.Name != null
? string.Format(EditorFeaturesResources.Error_creating_instance_of_CodeFixProvider_0, lazyFixer.Metadata.Name)
: EditorFeaturesResources.Error_creating_instance_of_CodeFixProvider;
errorReportingService.ShowErrorInfoInActiveView(
message,
new InfoBarUI(
WorkspacesResources.Show_Stack_Trace,
InfoBarUI.UIKind.HyperLink,
() => errorReportingService.ShowDetailedErrorInfo(ex), closeAfterAction: true));
foreach (var errorLogger in _errorLoggers)
{
errorLogger.Value.LogException(this, ex);
}
}
fixer = null;
return false;
}
}
private async Task AppendFixesAsync(
Document document,
TextSpan span,
......@@ -276,7 +340,7 @@ public async Task<Document> ApplyCodeFixesForSpecificDiagnosticIdAsync(Document
Func<string, IDisposable?> addOperationScope,
CancellationToken cancellationToken)
{
var hasAnySharedFixer = _workspaceFixersMap.TryGetValue(document.Project.Language, out var fixerMap);
var hasAnySharedFixer = TryGetWorkspaceFixersMap(document, out var fixerMap);
var projectFixersMap = GetProjectFixers(document.Project);
var hasAnyProjectFixer = projectFixersMap.Any();
......@@ -615,7 +679,7 @@ private async Task<IEnumerable<Diagnostic>> GetProjectDiagnosticsAsync(Project p
Document document, DiagnosticData diagnostic, CancellationToken cancellationToken)
{
var workspaceFixers = ImmutableArray<CodeFixProvider>.Empty;
var hasAnySharedFixer = _workspaceFixersMap.TryGetValue(document.Project.Language, out var fixerMap) && fixerMap.Value.TryGetValue(diagnostic.Id, out workspaceFixers);
var hasAnySharedFixer = TryGetWorkspaceFixersMap(document, out var fixerMap) && fixerMap.Value.TryGetValue(diagnostic.Id, out workspaceFixers);
var hasAnyProjectFixer = GetProjectFixers(document.Project).TryGetValue(diagnostic.Id, out var projectFixers);
// TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive
......@@ -755,18 +819,24 @@ private static ImmutableArray<string> GetAndTestFixableDiagnosticIds(CodeFixProv
private ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<DiagnosticId, ImmutableArray<CodeFixProvider>>>> GetFixerPerLanguageMap(
Dictionary<LanguageKind, List<Lazy<CodeFixProvider, CodeChangeProviderMetadata>>> fixersPerLanguage,
IExtensionManager? extensionManager)
Workspace workspace)
{
var fixerMap = ImmutableDictionary.Create<LanguageKind, Lazy<ImmutableDictionary<DiagnosticId, ImmutableArray<CodeFixProvider>>>>();
var extensionManager = workspace.Services.GetService<IExtensionManager>();
foreach (var languageKindAndFixers in fixersPerLanguage)
{
var lazyMap = new Lazy<ImmutableDictionary<DiagnosticId, ImmutableArray<CodeFixProvider>>>(() =>
{
var mutableMap = new Dictionary<DiagnosticId, List<CodeFixProvider>>();
foreach (var fixer in languageKindAndFixers.Value)
foreach (var lazyFixer in languageKindAndFixers.Value)
{
foreach (var id in this.GetFixableDiagnosticIds(fixer.Value, extensionManager))
if (!TryGetWorkspaceFixer(lazyFixer, workspace, logExceptionWithInfoBar: true, out var fixer))
{
continue;
}
foreach (var id in this.GetFixableDiagnosticIds(fixer, extensionManager))
{
if (string.IsNullOrWhiteSpace(id))
{
......@@ -774,7 +844,7 @@ private static ImmutableArray<string> GetAndTestFixableDiagnosticIds(CodeFixProv
}
var list = mutableMap.GetOrAdd(id, s_createList);
list.Add(fixer.Value);
list.Add(fixer);
}
}
......@@ -818,8 +888,9 @@ static ImmutableArray<IConfigurationFixProvider> GetConfigurationFixProviders(Li
}
}
private static ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<CodeFixProvider, int>>> GetFixerPriorityPerLanguageMap(
Dictionary<LanguageKind, List<Lazy<CodeFixProvider, CodeChangeProviderMetadata>>> fixersPerLanguage)
private ImmutableDictionary<LanguageKind, Lazy<ImmutableDictionary<CodeFixProvider, int>>> GetFixerPriorityPerLanguageMap(
Dictionary<LanguageKind, List<Lazy<CodeFixProvider, CodeChangeProviderMetadata>>> fixersPerLanguage,
Workspace workspace)
{
var languageMap = ImmutableDictionary.CreateBuilder<LanguageKind, Lazy<ImmutableDictionary<CodeFixProvider, int>>>();
foreach (var languageAndFixers in fixersPerLanguage)
......@@ -828,10 +899,15 @@ static ImmutableArray<IConfigurationFixProvider> GetConfigurationFixProviders(Li
{
var priorityMap = ImmutableDictionary.CreateBuilder<CodeFixProvider, int>();
var fixers = ExtensionOrderer.Order(languageAndFixers.Value);
for (var i = 0; i < fixers.Count; i++)
var lazyFixers = ExtensionOrderer.Order(languageAndFixers.Value);
for (var i = 0; i < lazyFixers.Count; i++)
{
priorityMap.Add(fixers[i].Value, i);
if (!TryGetWorkspaceFixer(lazyFixers[i], workspace, logExceptionWithInfoBar: false, out var fixer))
{
continue;
}
priorityMap.Add(fixer, i);
}
return priorityMap.ToImmutable();
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">položky z neimportovaných oborů názvů</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">Elemente aus nicht importierten Namespaces</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">elementos de espacios de nombres no importados</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">éléments provenant d'espaces de noms non importés</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">elementi da spazi dei nomi non importati</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">インポートされていない名前空間の項目</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">가져오지 않은 네임스페이스의 항목</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">elementy z nieimportowanych przestrzeni nazw</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">itens de namespaces não importados</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">элементы из неимпортированных пространств имен</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">içeri aktarılmayan ad alanlarındaki öğeler</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">未导入命名空间中的项</target>
......
......@@ -52,6 +52,16 @@
<target state="new">Downloading index failed:{0}</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider">
<source>Error creating instance of CodeFixProvider</source>
<target state="new">Error creating instance of CodeFixProvider</target>
<note />
</trans-unit>
<trans-unit id="Error_creating_instance_of_CodeFixProvider_0">
<source>Error creating instance of CodeFixProvider '{0}'</source>
<target state="new">Error creating instance of CodeFixProvider '{0}'</target>
<note />
</trans-unit>
<trans-unit id="Expander_display_text">
<source>items from unimported namespaces</source>
<target state="translated">來自未匯入命名空間的項目</target>
......
......@@ -139,14 +139,14 @@ public async Task TestGetCodeFixWithExceptionInRegisterMethodAsync()
[Fact]
public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds()
{
await GetDefaultFixesAsync(new ErrorCases.ExceptionInFixableDiagnosticIds());
await GetFirstDiagnosticWithFixAsync(new ErrorCases.ExceptionInFixableDiagnosticIds());
await GetAddedFixesWithExceptionValidationAsync(new ErrorCases.ExceptionInFixableDiagnosticIds());
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/21533")]
public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds2()
{
await GetDefaultFixesAsync(new ErrorCases.ExceptionInFixableDiagnosticIds2());
await GetFirstDiagnosticWithFixAsync(new ErrorCases.ExceptionInFixableDiagnosticIds2());
await GetAddedFixesWithExceptionValidationAsync(new ErrorCases.ExceptionInFixableDiagnosticIds2());
}
......@@ -154,23 +154,19 @@ public async Task TestGetCodeFixWithExceptionInFixableDiagnosticIds2()
public async Task TestGetCodeFixWithExceptionInGetFixAllProvider()
=> await GetAddedFixesWithExceptionValidationAsync(new ErrorCases.ExceptionInGetFixAllProvider());
private static async Task GetDefaultFixesAsync(CodeFixProvider codefix)
{
var tuple = ServiceSetup(codefix);
using var workspace = tuple.workspace;
GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager);
var fixes = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), includeConfigurationFixes: true, cancellationToken: CancellationToken.None);
Assert.True(((TestErrorLogger)tuple.errorLogger).Messages.Count == 1);
Assert.True(((TestErrorLogger)tuple.errorLogger).Messages.TryGetValue(codefix.GetType().Name, out var message));
}
[Fact, WorkItem(45851, "https://github.com/dotnet/roslyn/issues/45851")]
public async Task TestGetCodeFixWithExceptionOnCodeFixProviderCreation()
=> await GetAddedFixesAsync(
new MockFixer(),
new MockAnalyzerReference.MockDiagnosticAnalyzer(),
throwExceptionInFixerCreation: true);
private static Task<ImmutableArray<CodeFixCollection>> GetAddedFixesWithExceptionValidationAsync(CodeFixProvider codefix)
=> GetAddedFixesAsync(codefix, diagnosticAnalyzer: new MockAnalyzerReference.MockDiagnosticAnalyzer(), exception: true);
private static async Task<ImmutableArray<CodeFixCollection>> GetAddedFixesAsync(CodeFixProvider codefix, DiagnosticAnalyzer diagnosticAnalyzer, bool exception = false)
private static async Task<ImmutableArray<CodeFixCollection>> GetAddedFixesAsync(CodeFixProvider codefix, DiagnosticAnalyzer diagnosticAnalyzer, bool exception = false, bool throwExceptionInFixerCreation = false)
{
var tuple = ServiceSetup(codefix);
var tuple = ServiceSetup(codefix, throwExceptionInFixerCreation: throwExceptionInFixerCreation);
using var workspace = tuple.workspace;
......@@ -203,11 +199,12 @@ private static async Task GetFirstDiagnosticWithFixAsync(CodeFixProvider codefix
private static (TestWorkspace workspace, TestDiagnosticAnalyzerService analyzerService, CodeFixService codeFixService, IErrorLoggerService errorLogger) ServiceSetup(
CodeFixProvider codefix,
bool includeConfigurationFixProviders = false)
bool includeConfigurationFixProviders = false,
bool throwExceptionInFixerCreation = false)
{
var fixers = SpecializedCollections.SingletonEnumerable(
new Lazy<CodeFixProvider, CodeChangeProviderMetadata>(
() => codefix,
() => throwExceptionInFixerCreation ? throw new Exception() : codefix,
new CodeChangeProviderMetadata("Test", languages: LanguageNames.CSharp)));
var code = @"class Program { }";
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册