Skip to content

Commit

Permalink
Merge pull request #3652 from Evangelink/CA2248-notflags
Browse files Browse the repository at this point in the history
Update CA2248 to report on call to 'HasFlag' on enums not marked with…
  • Loading branch information
mavasani authored May 20, 2020
2 parents 1558e80 + 14378ba commit 03d8e7d
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1324,12 +1324,15 @@
<value>Consider using 'StringBuilder.Append(char)' when applicable.</value>
</data>
<data name="ProvideCorrectArgumentToEnumHasFlagDescription" xml:space="preserve">
<value>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</value>
<value>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</value>
</data>
<data name="ProvideCorrectArgumentToEnumHasFlagMessage" xml:space="preserve">
<data name="ProvideCorrectArgumentToEnumHasFlagMessageDifferentType" xml:space="preserve">
<value>The argument type, '{0}', must be the same as the enum type '{1}'</value>
</data>
<data name="ProvideCorrectArgumentToEnumHasFlagTitle" xml:space="preserve">
<value>Provide correct 'enum' argument to 'Enum.HasFlag'</value>
</data>
<data name="ProvideCorrectArgumentToEnumHasFlagMessageNotFlags" xml:space="preserve">
<value>This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,64 @@ public sealed class ProvideCorrectArgumentToEnumHasFlag : DiagnosticAnalyzer
internal const string RuleId = "CA2248";

private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentToEnumHasFlagTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentToEnumHasFlagMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
private static readonly LocalizableString s_localizableMessageDifferentType = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentToEnumHasFlagMessageDifferentType), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
private static readonly LocalizableString s_localizableMessageNotFlags = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentToEnumHasFlagMessageNotFlags), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));
private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ProvideCorrectArgumentToEnumHasFlagDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));

internal static DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
internal static DiagnosticDescriptor DifferentTypeRule = DiagnosticDescriptorHelper.Create(
RuleId,
s_localizableTitle,
s_localizableMessage,
s_localizableMessageDifferentType,
DiagnosticCategory.Usage,
RuleLevel.IdeSuggestion,
description: s_localizableDescription,
isPortedFxCopRule: false,
isDataflowRule: false);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
internal static DiagnosticDescriptor NotFlagsRule = DiagnosticDescriptorHelper.Create(
RuleId,
s_localizableTitle,
s_localizableMessageNotFlags,
DiagnosticCategory.Usage,
RuleLevel.IdeSuggestion,
description: s_localizableDescription,
isPortedFxCopRule: false,
isDataflowRule: false);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DifferentTypeRule, NotFlagsRule);

public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

context.RegisterOperationAction(context =>
context.RegisterCompilationStartAction(context =>
{
var invocation = (IInvocationOperation)context.Operation;
var flagsAttributeType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemFlagsAttribute);
if (invocation.TargetMethod.ContainingType.SpecialType == SpecialType.System_Enum &&
invocation.Arguments.Length == 1 &&
invocation.Instance != null &&
invocation.TargetMethod.Name == "HasFlag" &&
invocation.Arguments[0].Value is IConversionOperation conversion &&
!invocation.Instance.Type.Equals(conversion.Operand.Type))
context.RegisterOperationAction(context =>
{
context.ReportDiagnostic(invocation.CreateDiagnostic(Rule, GetArgumentTypeName(conversion), invocation.Instance.Type.Name));
}
}, OperationKind.Invocation);
var invocation = (IInvocationOperation)context.Operation;
if (invocation.TargetMethod.ContainingType.SpecialType != SpecialType.System_Enum ||
invocation.Arguments.Length != 1 ||
invocation.Instance == null ||
invocation.TargetMethod.Name != "HasFlag" ||
!(invocation.Arguments[0].Value is IConversionOperation conversion))
{
return;
}
if (!invocation.Instance.Type.Equals(conversion.Operand.Type))
{
context.ReportDiagnostic(invocation.CreateDiagnostic(DifferentTypeRule, GetArgumentTypeName(conversion), invocation.Instance.Type.Name));
}
else if (flagsAttributeType != null && !invocation.Instance.Type.HasAttribute(flagsAttributeType))
{
context.ReportDiagnostic(invocation.CreateDiagnostic(NotFlagsRule, invocation.Instance.Type.Name));
}
}, OperationKind.Invocation);
});
}

private static string GetArgumentTypeName(IConversionOperation conversion)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1443,15 +1443,20 @@
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagDescription">
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</target>
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessage">
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageDifferentType">
<source>The argument type, '{0}', must be the same as the enum type '{1}'</source>
<target state="new">The argument type, '{0}', must be the same as the enum type '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageNotFlags">
<source>This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</source>
<target state="new">This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagTitle">
<source>Provide correct 'enum' argument to 'Enum.HasFlag'</source>
<target state="new">Provide correct 'enum' argument to 'Enum.HasFlag'</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1443,15 +1443,20 @@
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagDescription">
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</target>
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessage">
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageDifferentType">
<source>The argument type, '{0}', must be the same as the enum type '{1}'</source>
<target state="new">The argument type, '{0}', must be the same as the enum type '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageNotFlags">
<source>This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</source>
<target state="new">This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagTitle">
<source>Provide correct 'enum' argument to 'Enum.HasFlag'</source>
<target state="new">Provide correct 'enum' argument to 'Enum.HasFlag'</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1443,15 +1443,20 @@
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagDescription">
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</target>
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessage">
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageDifferentType">
<source>The argument type, '{0}', must be the same as the enum type '{1}'</source>
<target state="new">The argument type, '{0}', must be the same as the enum type '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageNotFlags">
<source>This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</source>
<target state="new">This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagTitle">
<source>Provide correct 'enum' argument to 'Enum.HasFlag'</source>
<target state="new">Provide correct 'enum' argument to 'Enum.HasFlag'</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1443,15 +1443,20 @@
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagDescription">
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</target>
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessage">
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageDifferentType">
<source>The argument type, '{0}', must be the same as the enum type '{1}'</source>
<target state="new">The argument type, '{0}', must be the same as the enum type '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageNotFlags">
<source>This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</source>
<target state="new">This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagTitle">
<source>Provide correct 'enum' argument to 'Enum.HasFlag'</source>
<target state="new">Provide correct 'enum' argument to 'Enum.HasFlag'</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1443,15 +1443,20 @@
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagDescription">
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked. If these are different 'enum' types, an unhandled exception will be thrown at runtime.</target>
<source>'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</source>
<target state="new">'Enum.HasFlag' method expects the 'enum' argument to be of the same 'enum' type as the instance on which the method is invoked and that this 'enum' is marked with 'System.FlagsAttribute'. If these are different 'enum' types, an unhandled exception will be thrown at runtime. If the 'enum' type is not marked with 'System.FlagsAttribute' the call will always return 'false' at runtime.</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessage">
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageDifferentType">
<source>The argument type, '{0}', must be the same as the enum type '{1}'</source>
<target state="new">The argument type, '{0}', must be the same as the enum type '{1}'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagMessageNotFlags">
<source>This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</source>
<target state="new">This call will always returns 'false' because the enum type '{0}' is not marked with 'FlagsAttribute'</target>
<note />
</trans-unit>
<trans-unit id="ProvideCorrectArgumentToEnumHasFlagTitle">
<source>Provide correct 'enum' argument to 'Enum.HasFlag'</source>
<target state="new">Provide correct 'enum' argument to 'Enum.HasFlag'</target>
Expand Down
Loading

0 comments on commit 03d8e7d

Please sign in to comment.