Add an option to include extension methods from namespaces that are not imported in the rules using OverloadFinder - #1521
Merged
Conversation
… in MA0042 MA0042 and MA0045 only suggested the async equivalents in scope at the call site: an async extension method declared in a namespace that the file does not import was ignored, even though adding the using directive is enough to call it. OverloadFinder has a new IncludeExtensionMethodsFromNotImportedNamespaces option. When it is set, the candidates also contain the extension methods of the compilation and of its references that are accessible at the call site, apply to the receiver, and are not already in scope. They come from an index of the extension methods by name, built lazily once per compilation, and are returned after the candidates in scope. IsExtensionMethodFromNotImportedNamespace indicates whether a candidate requires a using directive. The analyzer still prefers an async equivalent in scope, and only suggests an extension method from a namespace that is not imported when there is none. The namespace is passed to the code fix, which adds the using directive next to the existing ones, at its sorted position when they are sorted, and keeps the file header at the top of the file. The code fix also qualifies an implicit receiver with "this", as an extension method cannot be called with a simple name: Do() became "await DoAsync()", which did not compile, and is now "await this.DoAsync()".
…in for the rules using OverloadFinder The rules using OverloadFinder to find an overload now have an option to include the extension methods declared in a namespace that is not imported: <RuleId>.include_extension_methods_from_not_imported_namespaces, false by default. MA0042 used to always include them; it now requires the option too. The option exists for MA0001, MA0002, MA0011, MA0032, MA0040, MA0042, MA0045, MA0074, MA0166, MA0167 and MA0210. When an analyzer reports two rules, the overloads are searched with the extension methods when the option is set for one of them, and the diagnostic is only reported when the option is set for the rule being reported. An overload that does not require a using directive is still preferred. OverloadFinder.GetNamespaceToImport indicates whether an overload requires a using directive, and the analyzers pass the namespace to the code fixes in the NamespaceToImport diagnostic property. The code fixes add the using directive with UsingDirectiveHelper, extracted from the MA0042 code fix. ArgumentListHelper binds the new invocation in a copy of the compilation where the document imports the namespace, as the overload is not in scope otherwise and the code fix would never be offered. MA0054 only searches constructors, and the MA0193 analyzer does not use OverloadFinder, so they have no option. The rule documentation lists the new options, as the documentation generator requires every configuration key to be documented.
This was referenced Sep 12, 2026
Closed
This was referenced Sep 28, 2026
Merged
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #1520.
Problem
The rules that suggest another overload only consider the overloads in scope at the call site. An extension method declared in a namespace that the file does not import is ignored, even though adding a
usingdirective is enough to call it:Changes
New option, opt-in, for every rule using
OverloadFinderusingUseStringComparisonAnalyzerUseStringComparerAnalyzer(invocations)UseIFormatProviderAnalyzerUseAnOverloadThatHasCancellationTokenAnalyzerDoNotUseBlockingCallInAsyncContextAnalyzerUseAnOverloadThatHasTimeProviderAnalyzerUseInKeywordForInParameterAnalyzerMA0045.…does not enable MA0042).usingdirective is always preferred.OverloadFinder). MA0079, MA0080 and MA0209 do not search overloads themselves; MA0079/MA0080 stay consistent with what MA0032/MA0040 report.OverloadFinderOverloadOptions.IncludeExtensionMethodsFromNotImportedNamespaces. When set (with aSyntaxNode), the candidates also include the extension methods of the compilation and of its references that are accessible at the call site, apply to the receiver, and are not already in scope. They are added after the candidates in scope and go through the same filters.GetNamespaceToImport(method, syntaxNode), returning the namespace to import when the overload requires ausingdirective. The analyzers pass it to the code fixes in theOverloadFinder.NamespaceToImportPropertyNamediagnostic property.Code fixes
UsingDirectiveHelper: adds theusingdirective next to the existing ones (innermost namespace declaration that has using directives, otherwise the compilation unit), at its sorted position (Systemfirst) when they are sorted, after the global using directives, keeping the file header at the top, with the line endings of the document. FixAll adds it once.ArgumentListHelper.AddArgument/GetTargetMethodaccept the namespace to import and bind the new invocation in a copy of the compilation where the document imports it. Without it, the overload is not in scope, the invocation does not bind, and the fix would never be offered.Do()becameawait DoAsync(), which does not compile for an extension method; the receiver is now explicit (await this.DoAsync()) when the method cannot be called with a simple name.Notes for reviewers
false. The first commit of this PR enabled the MA0042 suggestion unconditionally; the second commit makes it opt-in.OverloadOptionsdefaults: the call sites that usedoptions: defaultkeep a zero-initializedOverloadOptions(new OverloadOptions { … }), as its constructor defaults differ (e.g.AllowNumericConversion).usingdirective repeats a symbol lookup, only for the overload that is found.usingdirective. C# 14extension(...)block members are only suggested when their namespace is imported..editorconfigsection, as the documentation generator requires every configuration key to be documented.docs/README.mdis regenerated.Tests
usingdirective when enabled. Per-rule option tests for MA0045/MA0042, MA0001/MA0074, MA0032/MA0040 and MA0167/MA0166.usingplacement (sorted, no using directive with a file header, inside a namespace), FixAll, namespace imported in another file, candidates that are not async equivalents, implicit receiver.DocumentationGeneratorreports no changes.