Commit Graph
58 Commits
Author SHA1 Message Date
Siegfried Pammer 8faa1848d5 Close the test fixtures that hold an assembly open
A PEFile keeps its assembly mapped until it is disposed, and three fixtures
opened one and never did: DuplicateAssemblyReferenceTests both directly and
for every reference its resolver hands back, TypeForwarderResolutionTests for
everything the resolver probes, and LocationsInAstTests for the module behind
each decompiler. Their teardowns then deleted the directory those files live
in, which cannot work while the mapping is open, so a Windows run reported a
failed teardown for a run that otherwise passed. They now dispose what they
opened, the way WpfProjectExportTests already did.

Deleting is cleanup either way, so it goes through RepeatOnIOError rather than
straight at the file system, and that helper no longer rethrows: its last of
five attempts was deliberately unguarded, and four retries ten milliseconds
apart is a short window to wait for a handle that a virus scanner or the
indexer still holds. It now backs off to about a second and a half and, failing
that, reports and leaves the file behind, which costs nothing.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-09-19 08:43:07 +02:00
Siegfried Pammer 2115bd9028 Read the file system once per reference closure, not per reference
Resolving one assembly resolves its whole reference closure, and every
reference in it asked the same framework directories the same questions.
The worst of it was the scan for the closest version folder of a shared
framework: a directory listing plus a recursive file search, repeated per
reference and per runtime pack - 42 scans for two distinct answers when
decompiling ICSharpCode.ILSpyX.dll.

The scan result is only safe to keep for a bounded time: a runtime can be
installed or removed while ILSpy runs, and reloading an assembly list has
to see that. So it is kept for the length of an explicitly opened scope,
which the type system opens around the closure it resolves and closes
again afterwards; outside a scope the file system is read as before. The
scope owns what was read, so two of them on one resolver do not stack -
the first to end takes it, and the other reads the file system again.

BeginSnapshot is on IAssemblyResolver rather than an interface of its
own: it is core functionality of a resolver, and one implementation is
not an abstraction. This breaks the interface for implementors outside
this repository, who opt out by returning null - which is what the three
resolvers here that hold nothing do.

The remaining probes cost nothing to fix: the preferred runtime pack was
listed among the defaults it already belongs to, so its directory was
scanned twice for every reference that is not in it, and one package
folder was probed once per assembly the package contains.

Measured over 27 references with a fresh resolver each time: 3.3 ms per
assembly before, 3.1 ms without a scope, 1.1 ms with one.

Assisted-by: Claude:claude-opus-5:Claude Code
2026-09-04 19:55:39 +02:00
Siegfried Pammer af941339c0 Fix #2054: repair a chain of type forwarders that cycles
A chain of type forwarders is followed by assembly name, and every name resolves relative
to the assembly being decompiled - so a chain that leaves for another framework can be
pulled straight back into the directory it started in. A .NET Standard 2.0 assembly
sitting among .NET Framework 4.6.1 facades lost System.Linq.Enumerable that way: the
chain went netstandard -> System.Core (from the shared framework) -> System.Linq (back to
the input directory) -> netstandard, arriving at an assembly it had already passed
through. Nothing in the closure defines the type, so it stayed unknown and every LINQ
call decompiled as a static call with a delegate cast.

Once the closure is loaded, chains that return to an assembly they already visited are
walked a second time, resolving each hop next to the assembly that forwards it. The
assembly ending the repaired chain is loaded only once it is confirmed to declare the
type; it then wins the deduplication against the assembly of the same name it displaces,
which version order says nothing about. An assembly that neither forwards nor declares
the type ends the walk with nothing loaded, so a failed repair cannot displace anything.

Only chains that are already broken are walked twice. Preferring the forwarder's own
directory as a resolution policy was tried first and rejected: measured against a corpus,
it moved a .NET 8 facade's System.Runtime reference out of a net4x compilation, splitting
type identities so that overrides printed as virtual. The two cases cannot be told apart
where references are resolved, because that layer sees assembly names, not the type whose
chain is or is not terminating.

AssemblyReference now knows the module that declares it, which is what lets a hop be
resolved next to its forwarder, and its metadata reader is that module's.

A chain that cannot be repaired is reported in the reference load log the UI already
shows, once per reference: a facade forwards hundreds of types and they all fail together.

Assisted-by: Claude:claude-opus-5:Claude Code
2026-09-01 12:20:56 +02:00
Siegfried Pammer 76ec7588e3 #2764: Add regression test for duplicate assembly references
A merging obfuscator can leave a module referencing two versions of the same
assembly. Loading both split every type they declare into two definitions that
compare unequal, so a signature naming such a type through one reference stopped
matching a base method naming it through the other, and a genuine override was
printed as virtual. ac0ef8a11 (#3253) dropped the lower-version duplicates, but
nothing covered that, and neither of the existing fixture kinds can carry the
three assemblies the situation needs.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-08-25 22:46:59 +02:00
Siegfried Pammer 63900bb38c Align span conversions, betterness and inference with the C# 14 spec
Auditing against the first-class-span-types proposal turned up three
deviations, each now pinned by resolver unit tests whose expectations were
established by compiling probe programs with the C# 14 compiler.

Lower-bound type inference recursed into Span<T> targets as another
lower-bound inference, but Span<T> is invariant and the spec demands an
exact element inference there: M<T>(Span<T>, T) with (Span<string>, object)
must fail inference (CS0411), not unify to T=object.

Better-conversion-target compared ReadOnlySpan element types where the spec
compares the span types, admitting numeric and user-defined element
conversions the span types do not share: overloads taking ReadOnlySpan<int>
and ReadOnlySpan<long> are ambiguous (CS0121), not resolvable. The general
mutual-convertibility rule already implements the spec's span-type test, so
the element-level block is simply removed; the ReadOnlySpan-over-Span
identity rule stays, since it deliberately inverts that general rule.

The explicit span conversion did not exist at all, and with it the rule that
user-defined conversions are not considered between span-convertible types.
The visible consequence: string[] to Span<object> classified as an implicit
user-defined conversion via op_Implicit(object[]) plus array covariance,
where the compiler reports CS0266 - only the explicit span conversion
exists. Span conversions are also no longer considered for extension
receivers during method group conversion (CS0123), while invocations keep
them.

Part of #829.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-15 18:42:53 +02:00
Sebastien LebretonandCopilot 73c3283efa Model scoped parameter lifetimes
ScopedRefAttribute only records explicit syntax. Effective lifetime also
depends on UnscopedRefAttribute, params collections, out parameters, and
the defining module's RefSafetyRules version. Model those distinctions in
the type system without changing decompiler output.

Assisted-by: Copilot:gpt-5.6-sol:GitHub Copilot CLI
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 86d2918e-5a24-48b4-9a86-41d331ec3720
2026-08-14 15:49:25 +02:00
Siegfried Pammer 787cc71b3a Cover every rule of C# standard chapter 10 in the conversion tests
Audit the conversion test suites against the conversions chapter of the
draft-v8 C# standard and add tests for every rule that had none:
exhaustive implicit/explicit numeric conversion matrix, tuple/ValueTuple
and nullable-annotation identity, interpolated-string / throw-expression /
tuple-literal conversions, the boxing rule set including variance-based
boxing and unboxing, delegate-to-System.Delegate and IReadOnlyList<T>
reference conversions, type-parameter variance and effective-base-class
casts, generic method groups (inference, explicit type arguments, no
inference from the return type), the anonymous-function compatibility
checks CSharpConversions performs itself (via a LambdaResolveResult test
double), standard-conversion exclusion of user-defined operators, and
operators declared in base classes of the source type.

Two rules are implementation gaps rather than test gaps and get ignored
placeholder tests naming the gap: default literal conversions (10.2.16)
are an explicit TODO in CSharpConversions, and switch expressions
(10.2.18) have no ResolveResult representation. The full
section-by-section map is in
Analyses/ILSpy/2026-07-24_conversions-spec-coverage.md.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-25 08:09:14 +02:00
Siegfried Pammer 729fe03c29 Revive NRefactory explicit-conversion tests without resolver/AST
Convert the commented-out block in ExplicitConversionsTest the same way as
the implicit ConversionTest block: type-parameter casts via
DefaultTypeParameter with cross-referencing constraints, user-defined
operators as fixture types in the test assembly, constant sources via
ConstantResolveResult. The rr.Input asserts of the originals were resolver
artifacts and are dropped; UseDefinedExplicitConversion_Lifted instead
exercises the ResolveResult-based ExplicitConversion entry point.

Also extend PreferAmbiguousConversionOverReferenceConversion with the
overload-resolution half of the original NRefactory test (the ambiguous
conversion must not prevent M(BB) from being chosen over M(object)), which
the first revival pass had reduced to the conversion classification alone.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-25 08:09:14 +02:00
Siegfried Pammer ce7883fe22 Revive NRefactory conversion tests using ResolveResult/IType fixtures
The block of tests inherited from NRefactory's ConversionsTest was
commented out because it depended on ResolverTestBase (full AST +
CSharpResolver). Rewrite all of them against CSharpConversions directly:
hand-built MethodGroupResolveResults over fixture types compiled into the
test assembly (extension methods injected via the internal
extensionMethods field), DefaultTypeParameter instances with
cross-referencing constraints, and metadata-backed fixture interfaces for
the ExpansiveInheritance termination test.

Two deviations from the NRefactory originals:
MethodGroupConversion_RefArgumentObjectVsDynamic now expects a valid
conversion, because object/dynamic mismatch in a ref parameter is an
identity conversion and current csc accepts the assignment (verified);
PreferUserDefinedConversionOverReferenceConversion resolved an invocation,
so it is recast as an OverloadResolution test over FakeMethod candidates.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-25 08:09:14 +02:00
Siegfried Pammer ca9a9c4f40 Guard the TypeSystemAstBuilder nullable-array NRE with a regression test 2026-06-23 07:43:42 +02:00
Siegfried Pammer 77d9ee73e9 Fix IDE2000 globally 2025-12-29 19:24:31 +01:00
Siegfried Pammer 5f0e767c7d Fix crash in TryEncodingV2, if there are multiple extension groups with the same target type, but different extension parameter name. 2025-12-25 07:58:52 +01:00
Siegfried Pammer b403b7bb3d Fix #3542: Invalid explicit cast for implicit conversion to generic struct with interface type constraint 2025-10-09 08:24:49 +02:00
Peter Crabtree e1e16b64f5 dev: Strip BOM mark from text files 2025-08-28 20:56:19 -04:00
Siegfried Pammer 3b0939d99c Add ExtensionInfo: mapping of extension members to extension implementations and more. 2025-08-04 08:53:33 +02:00
Siegfried Pammer 68ae8ae833 Rename ITypeDefinition.HasExtensionMethods to ITypeDefinition.HasExtensions 2025-08-04 08:53:33 +02:00
Siegfried Pammer c642678f7f Remove ToTypeReference: Implement ParseReflectionName with SRM TypeName. 2025-08-02 15:32:11 +02:00
Siegfried Pammer 33e497eb80 Refactor ParseReflectionName to return IType instead of ITypeReference 2025-08-02 11:39:01 +02:00
Siegfried Pammer f673b7caa2 Add support for using different editions of .NET in unit tests 2025-07-04 19:08:07 +02:00
Siegfried Pammer 4bf9487ecd Remove IsRef, IsOut and IsIn flags from IParameter and Replace ParameterModifiers with ReferenceKind. 2024-07-19 18:40:07 +02:00
Christoph Wille fba0dbae8d Migrate to NUnit 4 (#3132)
* Migrate to NUnit 4 based on https://docs.nunit.org/articles/nunit/release-notes/Nunit4.0-MigrationGuide.html. Needed fix for https://github.com/nunit/nunit/issues/4564
* Test 4.1.0-adev.1 from NUnit MyGet feed (works locally in VS)
* Update to bugfix version adev.2 (works on GH)
* NUnit 4.0.1 final
2023-12-02 13:38:55 +01:00
Siegfried Pammer 8b0c7fbe85 Fix #2921: struct type definition contains duplicate default constructor. 2023-04-04 20:59:45 +02:00
Siegfried Pammer 73144e4a29 Port ReflectionHelperTests from NRefactory. 2022-09-13 12:57:04 +02:00
Siegfried Pammer 00edcad849 Use .NET 4.7.2 assemblies for our type system tests. 2022-03-07 09:19:24 +01:00
Siegfried Pammer 3f721a17c1 Add support for System.Runtime.CompilerServices.SpecialNameAttribute 2021-04-18 14:34:07 +02:00
dotnet format 0d9f871a4f #2128: Reformat the whole code base. 2020-08-28 01:05:38 +02:00
Siegfried Pammer 9c1bf7fbdf Introduce ReferenceKind in IParameter and ByReferenceResolveResult. 2019-07-25 23:23:44 +02:00
Siegfried Pammer 9d19b33ec0 Fix #1394: Fields marked 'specialname' are excluded from the type system 2019-01-26 15:31:47 +01:00
Siegfried Pammer 36baae8ff6 Fix #1365: Finalize method missing custom attributes. 2018-12-30 21:43:39 +01:00
Daniel Grunwald 72d755037b Fix #1283: handle invalid metadata when decoding constants 2018-12-21 16:45:56 +01:00
Siegfried Pammer 12122841c6 Fix bug in MetadataMethod.GetAttributes and add tests for PreserveSig in DllImportAttribute and PreserveSigAttribute. 2018-10-27 09:33:24 +02:00
Siegfried Pammer f33f26fddd Extend TypeSystem/TypeSystemLoaderTests and CSharpAmbienceTests. 2018-08-07 16:31:16 +02:00
Siegfried Pammer 9ed65dc780 Add more tests, fix typo. 2018-07-30 08:19:07 +02:00
Andrey Shchekin d15fe0f254 Fixed bug with decimal optional parameters not being resolved correctly.
Added option to uglify presentation of decimal constants (show `[DecimalConstant(...)]`).
2018-07-26 00:43:33 +12:00
Daniel Grunwald b396d203bd Merge IDecompilerTypeSystem with ICompilation. 2018-07-14 16:50:36 +02:00
Daniel Grunwald 3b46776c5e Rename IAssembly -> IModule.
While support for multi-module assemblies isn't fully working yet; it is clear at this point that we want
to treat each module in a multi-module assembly separately for the purposes of the type system.
2018-07-13 22:20:49 +02:00
Daniel Grunwald 2fa2eb9d18 Remove some of the old TS implementations. 2018-07-10 23:18:28 +02:00
Daniel Grunwald 3be697eadf Rewrite MinimalCorlib without using the unresolved TS. 2018-07-10 23:01:33 +02:00
Daniel Grunwald 4ec0028356 Don't cache attributes in the type system. 2018-07-01 16:53:57 +02:00
Daniel Grunwald 97fc614db5 Remove unimplemented IMember.IsShadowing and IField.IsFixed. 2018-06-28 22:31:55 +02:00
Siegfried Pammer 9d2957372b Undo reordering of base types 2018-06-28 16:39:31 +02:00
Siegfried Pammer 7d7b7b7d98 Fix order of DirectBaseTypes and typo in SpecializedParameter 2018-06-28 16:25:53 +02:00
Siegfried Pammer 2953c913d3 Fix handling of extension methods 2018-06-28 15:15:30 +02:00
Daniel Grunwald db47643a41 Interface implementations in TS 2018-06-27 23:02:24 +02:00
Daniel Grunwald ee3012281d Implement MetadataEvent and re-work the representation of attribute arguments. 2018-06-26 23:19:58 +02:00
Daniel Grunwald da06a48851 Implement more of the new metadata TS. 2018-06-25 23:48:49 +02:00
Daniel Grunwald 385433aff4 Add MetadataMethod. 2018-06-24 22:55:09 +02:00
Daniel Grunwald 0f8c310de2 Started work on new resolved TS implementation that directly uses SRM.
This is a work-in-progress; ILSpy is not functional with this commit.
The old code path still exists but is broken because some classes were modified for the new system.
The new system is still highly incomplete (types only have fields, but no methods).
2018-06-24 19:47:14 +02:00
Daniel Grunwald fa2df4df26 Clean up type system interfaces. 2018-06-20 21:29:32 +02:00
Daniel Grunwald 52e33a60c0 Disable tuple tests on SRM branch for now. 2018-06-02 12:47:40 +02:00