Skip to content

Report MA0179 on the array-returning GetCustomAttributes overloads - #1393

Merged
meziantou merged 1 commit into
mainfrom
feature/ma0179-getcustomattributes-arrays-6e5cea
Sep 6, 2026
Merged

meziantou merged 1 commit into
mainfrom
feature/ma0179-getcustomattributes-arrays-6e5cea

Conversation

@meziantou

Copy link
Copy Markdown
Owner

What

MA0179 (Use Attribute.IsDefined instead of GetCustomAttribute(s)) did not report the GetCustomAttributes overloads that return an array when they are followed by .Any() or .Count():

_ = member.GetCustomAttributes(typeof(ObsoleteAttribute), false).Any();   // was not reported
_ = member.GetCustomAttributes<ObsoleteAttribute>().Any();                // was reported

Why

IsGetCustomAttributesInvocation did operation as IInvocationOperation. The array-returning overloads (MemberInfo.GetCustomAttributes(Type, bool), the static Attribute.GetCustomAttributes methods, …) produce an implicit IConversionOperation (Attribute[]/object[] to IEnumerable<T>) around the invocation when they are passed to Enumerable.Any or Enumerable.Count, so the cast yielded null. Its singular twin IsGetCustomAttributeInvocation already called UnwrapConversions() first.

These are the overloads available on netstandard2.0 and the ones used by older code, so the rule was silent exactly where the older API is in use.

The .Length comparisons were already reported: there the array is the direct instance of the Length property reference, with no conversion in between.

Changes

  • UseAttributeIsDefinedAnalyzer: unwrap the conversions in IsGetCustomAttributesInvocation.
  • UseAttributeIsDefinedFixer: the same unwrapping in the Any() and Count() paths. Without it, the fixer would be offered on the newly reported patterns and return the document unchanged.
  • docs/Rules/MA0179.md: added the array-returning overload to the examples.
  • 4 tests: MemberInfo.GetCustomAttributes(Type, bool).Any(), Attribute.GetCustomAttributes(member, Type).Any(), .Count() > 0 with a named inherit argument, and a negative case checking that a user-defined GetCustomAttributes extension method followed by .Any() is still not reported (the conversion unwrapping must not weaken the check added in Do not report MA0179 on user-defined GetCustomAttribute(s) methods #1386).

Verification

  • Reverting only the two source files makes the 3 new positive tests fail and leaves the negative one passing; with the fix they all pass (33/33 in UseAttributeIsDefinedAnalyzerTests).
  • Full suite on all 5 Roslyn versions: 19278 passed, 0 failed.
  • dotnet run --project src/DocumentationGenerator exits 0 with no further markdown changes.

IsGetCustomAttributesInvocation cast the operation directly to
IInvocationOperation. The overloads returning an array (such as
MemberInfo.GetCustomAttributes(Type, bool) or the static
Attribute.GetCustomAttributes methods) are wrapped in an implicit
conversion when they are passed to Enumerable.Any or Enumerable.Count,
so the cast returned null and the rule reported nothing. Those are the
overloads available on netstandard2.0 and used by older code, so the
rule was silent exactly where they are used.

Unwrap the conversions first, as IsGetCustomAttributeInvocation already
does. The code fixer needs the same unwrapping in its Any() and Count()
paths, otherwise it would offer a fix that leaves the document
unchanged on the newly reported patterns.

The Length comparisons were already reported: the array is the direct
instance of the Length property, without a conversion.
@meziantou
meziantou merged commit 42631ad into main Sep 6, 2026
13 checks passed
@meziantou
meziantou deleted the feature/ma0179-getcustomattributes-arrays-6e5cea branch September 6, 2026 04:16
This was referenced Sep 6, 2026
This was referenced Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant