Skip to content

Make MSB4186 report resolved argument types and available overloads - #14281

Open
ViktorHofer with Copilot wants to merge 6 commits into
mainfrom
copilot/msb4186-improve-error-message
Open

Make MSB4186 report resolved argument types and available overloads#14281
ViktorHofer with Copilot wants to merge 6 commits into
mainfrom
copilot/msb4186-improve-error-message

Conversation

Copilot AI commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

When a static property function's arguments can't be matched to any overload, MSB4186 surfaced only the generic framework "method not found" message at the top level. When the mismatch came from a nested property function returning an unexpected type (e.g. due to a changed global build input), root-causing required expert debugging.

Context

The failing binder path in Expander.Function.LateBindExecute rethrew the original MissingMethodException, whose message says nothing about the types that were actually resolved or which overloads exist.

Changes Made

  • Expander.Function.cs — In the "no matching overload" branch of LateBindExecute, build an enriched message (GenerateNoMatchingOverloadMessage) listing the resolved argument types and the parameter types of every available overload; falls back to the original exception when no overloads can be described.
  • Strings.resx (+ .xlf) — New localizable InvalidFunctionNoMatchingOverload resource, embedded into the {1} slot of MSB4186.
  • Expander_Tests.cs — Added coverage asserting the reported argument and overload types.

Example — $([MSBuild]::Add($([System.DateTime]::Now), 1)) (a typed DateTime can't coerce to Add(double,double)/Add(long,long)) now yields:

MSB4186: Invalid static method invocation syntax: "[MSBuild]::Add($([System.DateTime]::Now), 1)". The arguments provided have the types "(System.DateTime, System.String)", but no overload of the method "Add" accepts those types. The available overloads have the following parameter types: Add(System.Double, System.Double), Add(System.Int64, System.Int64). ...

Testing

  • New unit test PropertyFunctionNoMatchingOverloadReportsTypes verifies both the resolved argument types and the overload parameter types appear in the error.

Notes

  • Purely additive to the error path; the original exception is preserved when no candidate overloads exist, so no behavior change for other failure modes.

Copilot AI linked an issue Jul 7, 2026 that may be closed by this pull request
Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
Copilot AI changed the title [WIP] Improve specificity of MSB4186 error message Make MSB4186 report resolved argument types and available overloads Jul 7, 2026
Copilot AI requested a review from ViktorHofer July 7, 2026 13:19
@ViktorHofer
ViktorHofer marked this pull request as ready for review July 7, 2026 13:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves diagnosability of MSB4186 when a static property function call fails to bind to any overload by reporting (a) the resolved argument types and (b) the available overload parameter-type signatures, plus adds unit coverage and a new localized string resource.

Changes:

  • Add GenerateNoMatchingOverloadMessage in Expander.Function.LateBindExecute to enrich the “no matching overload” MissingMethodException message.
  • Introduce new localized resource InvalidFunctionNoMatchingOverload in Strings.resx and several .xlf files.
  • Add unit test coverage validating that argument/overload types appear in the resulting MSB4186 message.
Show a summary per file
File Description
src/Build/Evaluation/Expander.Function.cs Generates a detailed “no matching overload” message listing resolved argument types and available overload signatures.
src/Build/Resources/Strings.resx Adds the new localizable string InvalidFunctionNoMatchingOverload used to enrich MSB4186.
src/Build.UnitTests/Evaluation/Expander_Tests.cs Adds a regression test validating the enriched MSB4186 output contains expected type information.
src/Build/Resources/xlf/Strings.cs.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.de.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.es.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.fr.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.it.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.ja.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.ko.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.pl.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.pt-BR.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.ru.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.tr.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.zh-Hans.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).
src/Build/Resources/xlf/Strings.zh-Hant.xlf Adds InvalidFunctionNoMatchingOverload trans-unit (new).

Copilot's findings

  • Files reviewed: 16/16 changed files
  • Comments generated: 2

Comment thread src/Build/Resources/Strings.resx
Comment thread src/Build/Resources/Strings.resx
…for MSB4186

Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>
@ViktorHofer

Copy link
Copy Markdown
Member

/review

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Expert Code Review (command) completed successfully!

Caution

agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation.

Details

The threat detection results could not be parsed.

Review the workflow run logs for details.

Backwards compatibility review complete. One MODERATE issue found: output format change on MSB4186's message body, plus missing ChangeWave gate required by evaluation engine policy.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Caution

agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation.

Details

The threat detection results could not be parsed.

Review the workflow run logs for details.

Code Simplification — ISSUE

SEVERITY: NIT

FINDING: The foreach loop in GenerateNoMatchingOverloadMessage manually builds a List<string> from an already-LINQ-filtered sequence, which is more verbose than necessary for a pure projection:

List<string> overloads = new();
foreach (MethodInfo method in _receiverType.GetMethods(_bindingFlags)
    .Where(m => String.Equals(m.Name, _methodMethodName, StringComparison.OrdinalIgnoreCase)))
{
    overloads.Add($"{_methodMethodName}({String.Join(", ", method.GetParameters().Select(static p => p.ParameterType.FullName))})");
}

RECOMMENDATION: Collapse to a single LINQ chain — this is an error path so LINQ overhead is acceptable, and the result is more direct:

List<string> overloads = _receiverType.GetMethods(_bindingFlags)
    .Where(m => String.Equals(m.Name, _methodMethodName, StringComparison.OrdinalIgnoreCase))
    .Select(m => $"{_methodMethodName}({String.Join(", ", m.GetParameters().Select(static p => p.ParameterType.FullName))})")
    .ToList();

Everything else — the ternary for providedTypes, the guard-clause early return on overloads.Count == 0, and the null-signal pattern in the caller — is clean and correct.

Generated by Expert Code Review (command) for #14281 · 1.6K AIC · ⊞ 30.1K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation.

Details

The threat detection results could not be parsed.

Review the workflow run logs for details.

Idiomatic C# Patterns — ISSUE


SEVERITY: MODERATE
FILE: src/Build/Evaluation/Expander.Function.cs
SCENARIO: Null checks in the new GenerateNoMatchingOverloadMessage method and its call site.
FINDING: Two null checks use the old == null / != null operators instead of the pattern-matching style that Rule 3 mandates and the CHECK list explicitly flags:

  • if (detailedMessage != null) at the call site
  • args == null in the ternary inside the method

RECOMMENDATION:

// call site
if (detailedMessage is not null)

// ternary guard
string providedTypes = args is null
    ? string.Empty
    : string.Join(...);

SEVERITY: NIT
FILE: src/Build/Evaluation/Expander.Function.cs
SCENARIO: String. (BCL type alias, uppercase) used throughout new code while immediately surrounding, unchanged code uses the lowercase string. keyword alias.
FINDING: New code uses String.Empty, String.Join (×3), and String.Equals, inconsistent with the adjacent string.Equals and the codebase-wide convention shown in Expander.cs.
RECOMMENDATION: Replace all four String. references with string.:

string.Empty
string.Join(", ", ...)
string.Equals(m.Name, _methodMethodName, StringComparison.OrdinalIgnoreCase)

Generated by Expert Code Review (command) for #14281 · 1.6K AIC · ⊞ 30.1K
Comment /review to run again

Comment thread src/Build/Evaluation/Expander.Function.cs Outdated
Comment thread src/Build/Evaluation/Expander.Function.cs
Comment thread src/Build/Evaluation/Expander.Function.cs
Comment thread src/Build.UnitTests/Evaluation/Expander_Tests.cs
@ViktorHofer

Copy link
Copy Markdown
Member

@copilot resolve the feedback from the Expert reviewer

… (MSB4186)

Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>

Copilot AI commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot resolve the feedback from the Expert reviewer

Addressed the Expert reviewer feedback in fa3b994: the two null checks in GenerateNoMatchingOverloadMessage and its call site now use is null / is not null pattern matching, per the repo convention.

I left the String.string. NIT unchanged: this file consistently uses the uppercase String. alias throughout (e.g. String.Empty/String.Equals at lines 209, 223, 438, 470, 539–541), so the new code matches the surrounding file convention.

@ViktorHofer

Copy link
Copy Markdown
Member

@copilot resolve the build failures (you will see them when you build the repo)

Co-authored-by: ViktorHofer <7412651+ViktorHofer@users.noreply.github.com>

Copilot AI commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot resolve the build failures (you will see them when you build the repo)

Fixed in 9ce43e1. The build failed with IL2080 (trimming) because GenerateNoMatchingOverloadMessage calls _receiverType.GetMethods(_bindingFlags) without the trimming suppression that the other public-only reflection call sites in this file carry. Added the same [UnconditionalSuppressMessage("Trimming", "IL2080:...")] attribute (with the identical justification: _bindingFlags is masked to AllowedBindingFlags, so it never carries BindingFlags.NonPublic and only binds public members of the property-function allowlist receiver). Microsoft.Build.csproj now builds clean (0 warnings/errors) and the NoMatchingOverload test passes.

@ViktorHofer
ViktorHofer requested review from a team and baronfel July 9, 2026 15:31
@baronfel

baronfel commented Jul 9, 2026

Copy link
Copy Markdown
Member

What does this error message look like in the console reporter vs terminal logger? Especially interested in potentially having a multiline layout for the overloads because of visual noise

@ViktorHofer

Copy link
Copy Markdown
Member

@copilot respond to @baronfel's question above.

Copilot AI commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot respond to @baronfel's question above.

@baronfel — the enriched text is currently a single line (overloads comma-separated), embedded in the {1} slot of MSB4186, so today it renders as one long sentence in both loggers. Here's how each would behave, and the tradeoff for going multiline:

Console / ParallelConsole logger (EventArgsFormatting.FormatEventMessage, src/Framework/Utilities/EventArgsFormatting.cs:348-364): splits the message on \n and re-emits each line as its own fully-prefixed error line — i.e. MyProj.csproj(3,5): error MSB4186: is repeated for every line. So today (single line) it's one line; if I introduced newlines to break up the overloads, this logger would repeat the whole file+code prefix on every overload, which is more visual noise, not less.

Terminal Logger (TerminalLogger.FormatEventMessage, src/Build/Logging/TerminalLogger/TerminalLogger.cs:1773-1796): has purpose-built multiline handling — when the message contains \n it prints the file(...) : error MSB4186: prefix once, then renders each subsequent line indented (TripleIndentation) under the project group (errors are accumulated per-project at :1453), and WrapText soft-wraps any line wider than Terminal.Width. It does not repeat the code prefix. So a multiline overload list would render cleanly here (one overload per indented line under the error).

Note one asymmetry: TL only enters the wrapping/indenting branch when the message already contains \n — a long single-line message isn't auto-wrapped with indentation in the error path, it's just emitted as one long line and left to the terminal emulator to soft-wrap.

So there's a real tension: a multiline layout looks great in TL but is noisier in the console logger, and because the overloads sit mid-sentence inside {1} today, injecting newlines mid-sentence is awkward for both. If a multiline layout is preferred I'd suggest restructuring so the overloads become a trailing block (each on its own line) rather than mid-sentence, and accepting the per-line prefix repetition in the legacy console logger as the cost. Happy to push that variant if you'd like it — let me know your preference and I'll adjust the resource + GenerateNoMatchingOverloadMessage accordingly.

@baronfel

Copy link
Copy Markdown
Member

Oof. It would be great if there was a way for us to not have to pick-and-choose which presentation(s) get preferential treatment. Structured messages might be great here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MSB4186 could be way more specific

4 participants