Report FS0039 only once for undefined type in inherit clause#19862
Report FS0039 only once for undefined type in inherit clause#19862T-Gro wants to merge 12 commits into
Conversation
…f unknown type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Multiple typecheck passes (EstablishTypeDefinitionCores FirstPass and SecondPass, plus Phase2AInherit's TcType/TcNewExpr) each emit the same FS0039 for an undefined base type in an 'inherit'/'interface inherit' clause, surfacing three identical diagnostics. Introduce a DedupInheritDiagnosticsLogger overlay scoped via UseTransformedDiagnosticsLogger to TcMutRecDefinitions. The dedup key is the formatted message of UndefinedName so duplicate FS0039s for the same identifier collapse to one while unrelated diagnostics, including FS0039 for distinct identifiers, pass through unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a cenv-scoped `(range, idText)` dedup of `UndefinedName` diagnostics applied around Phase 1F inherit type checking and Phase 2A `Phase2AInherit` processing in CheckDeclarations.fs. Resolves the triple-reporting in #16432 without changing the surface API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Switch reportedUndefinedNames to ConcurrentDictionary<struct (range * string), unit> for consistency with the sibling argInfoCache field and to be defensive against any future parallel cenv use. - Forward CheckForRealErrorsIgnoringWarnings to the wrapped logger so the InheritDedupDiagnosticsLogger is a faithful pass-through for everything other than the intended UndefinedName dedup. - Document the rationale for unwrapping only WrappedError in isDuplicateUndefinedName. Expert reviewer item 1 (merging suggestions from later UndefinedName into the first) intentionally not applied: in the inherit-clause case the colliding diagnostics carry identical suggestion sets, and merging would couple this site to NameResolution.AddResults internals. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
❗ Release notes requiredYou can open this PR in browser to add release notes: open in github.dev
|
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the InheritDedupDiagnosticsLogger sink-wrapper (PR #19862) with a per-cenv 'inheritResolutionFailed' marker. Phase 1D records (tycon.Stamp, synBaseTy.Range) when name resolution fails with UndefinedName. Phase 1F and Phase 2A consult the marker and skip re-resolution entirely, so the diagnostic is emitted once and the redundant work is eliminated. Non- UndefinedName failures (constraint, type-provider, etc.) continue to flow through normal recovery in every pass. Addresses review comment 3324874944 on PR #19862. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Extend isUndefinedNameFailure to also unwrap the constraint-solver wrappers (ErrorFromAddingTypeEquation, ErrorFromAddingConstraint, ErrorFromApplyingDefault) so the marker is set when UndefinedName surfaces from the CheckCxs second pass via a generic inherit-clause type-argument failure. - Remove a misplaced /// doc comment from 'type cenv = TcFileState'. - Collapse three duplicated InheritsDeclarations regression Fact methods into a single Theory + InlineData driving an assertSingleFS39 helper. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Delete .tools/ralph/research-notes.md (agent scratchpad, not for repo). - Replace isUndefinedNameFailure helper + XML doc with active pattern (|UndefinedNameError|_|) co-located with the catch. - Extract tryResolveInheritType local in EstablishTypeDefinitionCores so the call site reads as a single mapFold. - Drop narrative block comments at both Phase2A and Phase 1D/F sites; the function name and pattern carry the intent. - Trim 5-line XML doc on inheritResolutionFailed to a one-liner. - Rewrite tests with strict withDiagnostics asserting exact code, range, and message for the full diagnostic set (no per-code filtering).
auduchinok
left a comment
There was a problem hiding this comment.
The active pattern for undefined name errors captures unrelated errors which is wrong. The whole approach seems to still do the same analysis multiple times and these changes just try to hide this problem.
|
|
||
| argInfoCache: ConcurrentDictionary<string * range, ArgReprInfo> | ||
|
|
||
| /// Inherit clauses whose type already failed UndefinedName; skip re-resolution to avoid duplicate FS0039. |
There was a problem hiding this comment.
The comment duplicates the signature.
| | UndefinedName _ -> Some () | ||
| | WrappedError(inner, _) | ||
| | ErrorFromAddingTypeEquation(error = inner) | ||
| | ErrorFromAddingConstraint(error = inner) |
There was a problem hiding this comment.
This is definitely not a undefined name error.
| if cenv.inheritResolutionFailed.ContainsKey(struct (tcref.Stamp, synBaseTy.Range)) then | ||
| mkUnit g m, tpenv | ||
| else |
There was a problem hiding this comment.
I'm not sure I'm following the idea. If there was an error previously then we skip the analysis and if there wasn't we still do it the second time? Why do we need to resolve the same identifiers more than once at all?
Fixes #16432
inherit T()with undefinedTemitted FS0039 three times — one pertype-checking pass over the inherit clause. Now emitted once.