Repository navigation
release: 2.3.1 - #258
Merged
Merged
release: 2.3.1#258
Conversation
…ne to 2.3.0 2.3.1 is a patch of 2.3.0 (tag v2.3.0 at edeb90c): release/2.3.1 is cut from that tag, per decision 94's patch release, and carries only fixes for bugs that shipped in 2.3.0. Version is 2.3.1, and the changelog opens its section as Unreleased with its first line, since pack fails while the section is empty. Package validation compares with 2.3.0, as release/2.4.0 does, so a fix here can't change what 2.3.0 ships. Closes #199 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a value object reaches it CMTK1011, and CMTK1008 for comparison, listed the members of the value object and its base records. An interface's explicit implementation of a member the generators implement is the default every implementing type reaches until it declares a public member, so the generated one replaced it without a word: Max<Bounded>() read the generated MaxValue where IBounded<TSelf>'s threw, and an IFormattable call printed 1 where the interface answered its own text. Metalama lists no private member of a type in another assembly, so a base record from a referenced project was missed too, replaced (IFormattable) or kept answering beside the generated member (IMinMaxValue). ExplicitImplementationsOf now asks the interface mapping (TryFindImplementationForInterfaceMember) for each member of a generated interface the type implements, and refuses an explicit implementation, naming where it is: "in its base type X" or "in its interface X". An interface's == maps to the record's own, so it stays allowed. INumberBase<TSelf>'s UTF-8 bridges, the only such members in .NET's interfaces, are not refused, compared by assembly symbol. IConvertible declared through an interface, and a closed set's IParsable<TSelf> through one, are CMTK1011 where the aspect crashed (LAMA0041, LAMA0512). Decision 121 records it, an exception to decision 34 under decision 119. Input class (GTS): an explicit implementation of a generated interface's member, by where it sits. Catalogue in BuildOutcomeTests: the eleven interfaces of #191 on a record struct, plain and validated; two interfaces down; through a base record's interface; on a record class over string; from another assembly (interface and base record, in Generators.Probes); a closed set through an interface of either assembly. Four controls build: a ToString() seam, a Uri's missing ordering, an interface's ==, an INumberBase<TSelf> value object. Siblings found and fixed here: another assembly's base record (missed since phase 11), IConvertible through an interface (LAMA0041), a closed set's IClosedSet<TSelf> and IParsable<TSelf> through an interface. ExplicitlyImplementsEquatableBetween has no gap: a record base's synthesized Equals(TBase) wins over any interface default. Left for its own issue: IValueObject<,> through a user interface still fails with LAMA0512. Reverting the three source files fails 30 of 263 BuildOutcomeTests; each guard's own seed fails only its rows. Cherry-picked from 466c541 (#193, on release/2.4.0) for 2.3.1, since the defect shipped in 2.3.0: decision 121 (124 on release/2.4.0, renumbered here so 2.3.1's records run without a gap) records the exception to decision 34 as taken in the patch, and the changelog line sits in 2.3.1's section. Refs #191 Defect-Class: GTS Found-By: VA Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A value object over a base record could declare an Equals of a deeper base's type, such as Equals(DeepBase?). It implemented IEquatable<DeepBase>, which the value object lists again through IReimplementedEquatable, so a HashSet<DeepBase> compared by it, while == compared the wrapped value: two holding "a" were equal, and the set held both. EqualityDeclaredByHand refused Equals(TSelf) alone, and IntroduceEquality skipped its own new Equals(TBase) because the value object declared one. EqualityDeclaredByHand now refuses an Equals of the value object's type or of any of its base records' types, nullability aside, with the existing CMTK1011 and its remedy (normalise in Create). One of the direct base's type is C#'s own CS0111, beside the override the record synthesizes (measured). Input class (GTS): the parameter type of a hand-written Equals. Catalogue: the value object's own on a record struct and on a record class (nullable parameter, already refused), a deeper base's (refused now), and the direct base's (C#'s CS0111). The record class case had no fixture; it has one now, and no sibling was found. The test fails before the fix and with the base types dropped from the check (1 of 233 BuildOutcomeTests). Refs #196 Defect-Class: GTS Found-By: OR Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… one as such A base record's public member the generators introduce counted for nothing. Its static MinValue and MaxValue, with IMinMaxValue<TSelf> or without, were the value object's, and no MinValue or MaxValue was generated; its public Value, From, Parse, TryParse, ToString(string?, IFormatProvider?), TryFormat or CompareTo(object) failed the aspect (LAMA0500), naming nothing the user wrote. From another project, a public static member that implements a static abstract one reads as an explicit implementation, since the compiler records every such implementation as one, so the error called it "the explicit implementation of ...". ImplementationsOf (was ExplicitImplementationsOf) now refuses any implementation the interface mapping finds outside the value object's own public members, public or explicit, and names a public one by its type and member. The names and signatures checked on the value object are checked on each base record too, for a member that implements no interface. MembersNamed leaves an explicit-looking member to the mapping, so a member of another assembly is named once. The build-outcome fixture builds a library that references the base package alone, beside its consumers, since a base type there that carries a marker or IClosedSet<TSelf> is no value object or set of its own. A closed set over its base class that implements IClosedSet<TSelf> explicitly, an outside review's report, is refused since 4a3d9a6; the test holds it now. Input class (GTS): a generated member declared in a base record, by member and by where the base is. Catalogue: MinValue/MaxValue through IMinMaxValue and by name alone, ToString(string?, IFormatProvider?), CompareTo(object), Parse/TryParse, Value, From and TryFormat, in this project, and a public static from the library. A base's TryFrom, a seam, stays with #194. Reverting this commit's source fails 12 of 274 BuildOutcomeTests; each guard's own seed fails only its rows (the mapping's public case, the names on base records, the public naming, the explicit-looking names). Refs #197, #191 Defect-Class: GTS Found-By: VA Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…de the value object's Where a validated value object reaches the marker only through a base record of another project that implements Create explicitly, every generated way in called the value object's own Create, which accepted "x", and generic code calling T.Create reached the base's, which refused it. CreateDeclarations read the value object's own methods, and Metalama lists no private member of another assembly's type. CreateDeclarations asks the interface mapping where T.Create lands. An explicit implementation in a base record or an interface is CMTK1011, and so is a base's public Create beside the value object's own; a base's public Create alone is the one rule set the generated code calls too, and builds. The error says to declare the marker on the value object itself, which re-implements it, since the base cannot be changed from there. Input class (GTS): where the Create that T.Create reaches is declared. Catalogue: an explicit one in the library's base with and without the value object's own, a public one beside it, and, building, the marker declared again over the explicit base and a public base Create alone. In the same project such a base carries the marker and is CMTK1022. Reverting the CreateDeclarations mapping fails the three refusal rows; dropping the public-alone condition fails OnlyPublicBase. Refs #195 Defect-Class: GTS Found-By: OR Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build: open 2.3.1 from v2.3.0, and move the package-validation baseline to 2.3.0
A base record's CompareTo(TSelf) was not seen as the comparison seam: DeclaresCompareTo read the value object's own methods, so no [HandWrittenOrdering] was put on it and an EF Core query ordered by the column ([1, 2]) where memory ordered [2, 1]. The generated CompareTo(TSelf) was not introduced, since OverrideStrategy.Ignore saw the inherited one, and the generated operators and CompareTo(object) called left.CompareTo(right), which C# binds to the generated CompareTo(object), declared on the more derived type: < overflowed the stack and ended the process. A base's TryFrom(T) and Revalidate() were the value object's in the same way, and the generated ones, which run Create, were not introduced. A base's CompareTo(object) failed the aspect (LAMA0500). HandWrittenComparisonMembers reports a base record's CompareTo(TSelf) and CompareTo(object) (CMTK1008), named once where the interface mapping finds the second too. ValueObjectDeclaration.InheritedSeams reports a base's TryFrom(T) and Revalidate() on a validated value object (CMTK1011), whose remedy says to declare it on the value object. A base's FromKnownGood(T) is another signature than the generated one, which takes the caller's expression too and hides it, so it stays allowed. Decision 121 records the choice over taking a base's as the seam. Input class (GTS): a seam, by where it is declared. Catalogue: each seam in a base record (CompareTo(TSelf), CompareTo(object), TryFrom, Revalidate, FromKnownGood), beside the value object's own seams the suite already holds. A base's sealed ToString() stays the seam. Reverting the source to 03e199b fails 6 of 285 BuildOutcomeTests; each guard's own seed fails only its rows, the named-once check included. Refs #194 Defect-Class: GTS Found-By: OR Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…face-explicit-implementation-refused-2-3-1
…written-equals-tbase-refused-2-3-1
…implementation-refused-2-3-1 fix: refuse a generated member written by hand wherever a value object reaches it
…-tbase-refused-2-3-1 fix: refuse a value object's Equals of a base record's type
…sed-2-3-1 fix: refuse a seam a base record declares in place of the value object
…a base record
IValueObject<,>, IValueObjectMaterializer<,> and IPlainValueObject<,>
are implemented by the generators with OverrideStrategy.Fail, and
declaring one on the value object is CMTK1011. Declared through an
interface of the user's, as an IEntityId<TSelf> : IValueObject<TSelf,
Guid> written to constrain generic code, or by a base record, the
aspect failed instead (LAMA0512, and LAMA0041 for the materializer),
naming nothing the user wrote: GeneratedMembersDeclaredByHand read the
value object's directly declared interfaces alone.
It now reads each declared interface's own interfaces and each base
record's, and names where the contract comes from ("the interface
IValueObject<OrderId, Guid> in its interface IEntityId<OrderId>", "...
in its base type X"), with IConvertible folded into the same check.
Neither marker derives from a contract, so an interface derived from a
marker stays allowed. Only builds that failed before fail, now with a
CMTK error. Decision 121 records refusing over accepting the contract
through an interface, which would add behaviour to a patch.
Input class (GTS): a contract the generators implement, by where it is
declared. Catalogue: IValueObject one and two interfaces down, through a
base record's interface and through a library interface;
IValueObjectMaterializer and IPlainValueObject through an interface; and
an interface derived from the marker, which builds. IConvertible's rows
hold the folded check.
Reverting the source fails the 6 new rows and the two LAMA guards (8 of
294 BuildOutcomeTests); dropping the interface or the base read fails
its rows; counting the marker as a contract refuses every value object.
Refs #192
Defect-Class: GTS
Found-By: VA
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ct-through-interface-refused-2-3-1 fix: refuse a value-object contract declared through an interface or a base record
…rameter, as CMTK0001 does CMTK0005 and CMTK0006 decided "no default" with IsNoDefaultStruct, which admitted a type parameter only beside struct. CMTK0001 and CMTK0009 use DefaultRestriction, which also admits a parameter bound by the marker beside new() and no class bound: a generated class has no public parameterless constructor, so such a T is a struct value object. So for where T : IEntityId<T>, new(), new T() was CMTK0001 while new T[3] and an unassigned T field reported nothing, although the README defines both rules over CMTK0001's list. IsNoDefaultStruct is now DefaultRestriction(type) is not null, the predicate CMTK0001 reads. Both rules are Warnings, which the README lets learn forms in a minor version. Input class (AIS): what the judged type is. Catalogue: TypeArgumentKinds, 80 kinds with the one added here (a new() parameter bound by an interface deriving from IValue<int>, the reported shape). NoDefaultKindTests runs CMTK0001, CMTK0005 and CMTK0006 over every kind against one oracle, the README's list; CMTK0001 agreed on all 80 before the fix, and the two new() kinds were the only rows where the other two did not. No other sibling: only these two rules called the narrow predicate. Reverting the source fails the 4 new() rows of 240. Refs #207 Defect-Class: AIS Found-By: CA Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…jection boxed first Select(r => (object)r.Rank).Max(), .Min(), .Order(), .OrderDescending() and .OrderBy(x => x) over a value object whose ordering is its own CompareTo were not refused: EF dropped the cast and answered the column's order, Max 3 and Order 1, 2, 3 where memory answered 1 and 3, 2, 1 for a descending CompareTo (measured on SQLite). The Finder judged an ordering operator by its declared type, object here, and looked under a conversion only in a key selector's body, which 8f6af8b added for OrderBy(r => (object)r.Rank). The Finder now reads the types a sequence's elements have under their boxing: a Select's body unconverted, followed through the operators that pass elements on (Where, Take, Distinct, the orderings, Cast and OfType, Concat and its kind). An operator without a key, or whose key is the element itself, is refused by those types. Query filters go through the same Finder, so a filter that does this is refused when the model is built. The README says the cast changes nothing. Input class (EFT): an ordering operator over a value object, by where the conversion sits. Catalogue: QueryShapes' Op axis gains OrderBoxed, OrderByItselfBoxed, MinBoxed and MaxBoxed over every wrapped kind, converter and accessor of the column and nullable sites (green on SQLite, PostgreSQL's translation and SQL Server). Explicit rows hold OrderDescending, a record class, a navigation's Enumerable.Max, a Where and Take between the box and the operator, and a query filter. Sibling found: over a navigation, the same query threw a NullReferenceException inside EF rather than answering; it is refused before EF now. Reverting the source fails 33 of 11095 (24 catalogue rows, 9 explicit). Refs #206 Defect-Class: EFT Found-By: CA Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, and keep its sealed ToString() ClosedSetTypes read the set's own members alone. A base class's Parse(string), Parse(string, bool), TryParse(string?, bool, out TSelf), Match, a static All, a protected _name, an IParsable<TSelf> it implements, and its sealed ToString() failed the aspect (LAMA0500, LAMA0502, LAMA0503, LAMA0512), naming nothing the user wrote, in this project and from another. A base class's ToString() that is not sealed was replaced by the generated one without a word, where a value object's base record's has been CMTK1011 since phase 11. The checks of names and signatures now read each base class, private members aside (a base's private _name and _all built and still do), and an IParsable<TSelf> a base implements is named with its base. A base's sealed ToString() is the seam, through ValueObjectDeclaration's own check; one not sealed is CMTK1011 with the value objects' remedy. Decision 121 records refusing it over taking it as the seam, which would change what such a set prints in a patch, and the exception to decision 34: such a set built on 2.3.0. CS0738 joins CS0535 as what a refused set may leave, where a base's All has another type. Input class (GTS): a generated closed-set member, by where it is declared. Catalogue in BuildOutcomeTests: each member above in a base class of this project, Parse, IParsable and both ToString() forms in a library base, QualifiedName (refused before, through the interface mapping), and four that build: a sealed ToString() of either assembly, private fields, a [JsonConverter] on the base. InheritedMembersTests holds what a set over a sealed ToString() prints. Siblings found by the catalogue: All, Match, Parse(string, bool), TryParse(string?, bool, out) and a protected _name, beyond the three the report named. Removing the base reads and the ToString() refusal fails the 11 new refusal rows and the two LAMA guards (13 of 310 BuildOutcomeTests); reverting the seam fails the test project's build on MaskedSet. Refs #205 Defect-Class: GTS Found-By: CA Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rojection's member
OrderBy(r => (object?)r.Optional ?? r.Rank) and
Select(r => new { Key = (object)r.Rank }).OrderBy(a => a.Key) over a
value object whose ordering is its own CompareTo were not refused: EF
dropped the cast and answered 1, 2, 3 where memory answered 3, 2, 1 for
a descending CompareTo (measured on SQLite at 3d2abfd). The Finder looked
under a conversion only at the top of a key's body or a Select's, so a
?? over the box, or a member of a projection that holds it, read as
object.
The Finder now reads the types a key or selector body yields under its
boxing: both sides of a ?? and a ?:, the elements for the lambda's
parameter (as before), and for a member of a projection, the expression
its new or member initializer set it from, found in the same lambda or
in the Select that built the elements, through nested projections and
the operators that pass elements on. The README, its claim and the
changelog line name both shapes.
Input class (EFT): an ordering operator over a value object, by where
the boxing sits and how the key reads it back. Siblings found by the
same probe and fixed here (VA): Max(a => a.Key) and
Select(a => a.Key).Max() over the projection, a nested anonymous member,
a member-init type, a Where between the projection and the ordering, a
?? inside the projection; each answered the column's order. A ?: key
failed translation on SQLite and is refused before it now. Over a
navigation the projected Max threw a NullReferenceException inside EF;
it is refused before EF. Catalogue: QueryShapes' Op axis gains
OrderByCoalescedBoxed, OrderByProjectedBoxed and MaxProjectedBoxed over
every kind, converter and accessor of the column and nullable sites.
Explicit rows hold the anonymous shapes, the navigation and a query
filter; controls over .Value and a generated ordering still translate.
Reverting the source fails 29: 18 catalogue rows, 11 explicit.
Refs #206
Defect-Class: EFT
Found-By: OR
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…query's First carries Eight shapes still answered the column's order at d573eef, 1, 2, 3 (Max 3) where memory answers 3, 2, 1 (Max 1) for a descending CompareTo, measured on SQLite: a grouping's boxed key under OrderBy(g => g.Key), a grouping's boxed elements under g.Max(), GroupBy's, Join's and SelectMany's result selectors holding the boxed value, SelectMany of boxed values under Max(), and a subquery's or a navigation's boxed First() as a key. The Finder followed a lambda's parameter only through Select and the operators that pass elements on, and visited a nested operator, g.Max(), knowing nothing of its source. The Finder now knows what each lambda parameter stands for. Every LINQ operator binds its lambdas' parameters: to the source's elements for a selector, key or predicate; to each side's elements for Join and GroupJoin (the inner side whole for GroupJoin); to the collection's elements for SelectMany's result selector; to the key and the grouped elements for GroupBy's. A grouping's Key reads its key selector, and First, Single, Last, ElementAt and their OrDefault forms read the element of their source. The visitor carries these bindings into nested lambdas, so an operator inside one reads what its source was built from. The README, its claim and the changelog line name the new shapes. Input class (EFT): an ordering operator over a value object, by the operator whose lambda carries the boxed value to it. Sibling found by the same rows (VA): a GroupJoin result reading cs.First().Rank, refused before EF now. Loud and left as it is: a Tuple.Create projection, which EF does not translate. Catalogue: QueryShapes' Op axis gains OrderByGroupKeyBoxed and OrderByJoinedBoxed over every kind, converter and accessor of the column and nullable sites. Controls over .Value and a generated ordering, through a grouping, a join and a subquery, still translate. Reverting the source fails 21: 12 catalogue rows, 9 explicit. Refs #206 Defect-Class: EFT Found-By: RS Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mension AnalyzeArrayCreation skipped an array only when every dimension was a constant 0. A multidimensional array is empty when any one dimension is, so new Plain[0, 2] held no default element and was reported. The check is now Any; stackalloc reads the same rule, where its one dimension made All and Any agree already. Input class (AIS): an array creation of a no-default struct, by rank and by which dimension is a constant zero. The new row covers rank 2 with the first, the second and both dimensions zero, a variable beside a literal zero and a named constant beside a variable, rank 3 with a zero in the middle and last, a jagged [0][], and two arrays with no zero dimension that still report. No sibling: the invocation forms take one length, where a zero is already skipped. Reverting the source fails the row: 8 reports where 2 are expected. Closes #227 Defect-Class: AIS Found-By: OR Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The zero check matched only a boxed int. An array dimension may be a long, uint or ulong, so new Plain[0L] was reported, and a stackalloc size is read from syntax with its type before the conversion to int, so stackalloc Plain[(byte)0], [(short)0] and ['\0'] were too. One IsZero now reads every integral constant, for the array creation, the stackalloc and the allocating calls alike. Found running #227's input class (an array creation by which dimension is a constant zero) across how the zero is written: int, long, uint, ulong, byte, short and char, in new, stackalloc, GC.AllocateArray and Array.Resize. The six that failed are the long and unsigned dimensions and the three stackalloc sizes. Seeding the int-only match back fails the row: 8 reports where 2 are expected. Refs #227 Defect-Class: AIS Found-By: VA Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HasQueryFilter(r => r.Rank == Limits.Max()) over a value object whose ordering is its own CompareTo was refused when the model was built, though EF evaluates Limits.Max() in memory, by CompareTo, and sends the answer as one parameter. The Finder refused an ordering operator in a filter without asking whether it runs in memory, as its comparison branches do. It now lets an ordering through in a filter when it reads nothing of the database and takes no lambda. The lambda matters: measured on SQLite, EF evaluates Limits.Max(), Min(), Order().First() and OrderDescending() .First() in memory, but sends the values to the database and orders them there for OrderByDescending(l => l).First(), which kept the row ranked 3 where memory keeps 1, and fails to translate MaxBy(l => l). So #225's proposed change, skipping by database reads alone, would have answered those silently; they stay refused. KB-058 pins the distinction with plain EF (a collection with a lambda goes into the SQL as a VALUES list of parameters; Max(v => v) throws inside EF). The README says when a filter may order the context's values (claim E05-query-filter-own-values). Input class (EFT): an ordering operator in a query filter, by its source: the context's values without a lambda (allowed: Max, Min, Order().First(), OrderDescending().First() over an array and a list), the context's values with a lambda (refused: OrderByDescending(l => l), MaxBy, a boxing Select before Max, which EF happens to evaluate but which the lambda rule keeps refused), the entity's navigation and the table itself (refused). Reverting the source fails the 4 allowed rows; dropping the lambda condition, the change #225 proposed, fails the 3 refused ones. Refs #225 Defect-Class: EFT Found-By: OR Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ref kind ValueObjectDeclaration.MethodsWithSignature and ClosedSetTypes.SameParameters matched a hand-written method to a generated one by name and parameter types alone. A generic Parse<TFormat>(string, IFormatProvider?), a From(in int) or From(ref int), a TryParse whose last parameter is by value, a generic or by-value TryFormat, and a closed set's generic Parse or by-value TryParse were refused as the generated member written by hand (CMTK1011), on the type and on a base record or base class, though C# overloads by each and Metalama introduces the generated member beside it (measured in a scratch consumer: every row builds and calls the generated member, no LAMA error). Both now match only a method without type parameters, and compare each parameter's ref kind: by reference where the generated one is out, by value where it is by value. ref or in against out is no overload in C# (CS0663) and nullability none at all, so those stay CMTK1011. Input class (GTS): a method that shares a generated member's name and parameter types, by what differs (arity; in, ref or out against by value; ref and in against out; nullability) and where it is declared (the type, a base record of this assembly and of another, a closed set and its base class), over the declaration shapes the check reads (record struct, sealed record class, closed set). 34 rows in OverloadTests build and call the generated member, 10 reach the hand-written overload, and 8 controls in BuildOutcomeTests stay refused. The wrapped type only enters the check as From's parameter, so the rows wrap int, which has every generated parse and format. Siblings found by the same catalogue on the other signature checks (TryFrom, Revalidate, CompareTo, Equals, GetHashCode, PrintMembers, ToString()) are the next commit. Reverting the source refuses all 16 new value objects and sets in the test project (32 CMTK1011); comparing ref kinds exactly (ref against out as another method) fails the 6 ref and in controls and Every_error_is_one_of_ the_intended_ones. FabricTests looked up From and TryFrom by name, which an overload made ambiguous; it looks them up by signature. The surface snapshot renders a generic method's type parameters and a ref or in parameter, so an overload no longer reads as a duplicate line; Match gains <TResult>. Closes #226 Defect-Class: GTS Found-By: OR Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pe and ArrayPool<T>.Rent AnalyzeInvocation looked only at generic calls with one type argument, GC.AllocateArray, GC.AllocateUninitializedArray and Array.Resize. The README defines CMTK0005 as an array of a value object created with a length, which Array.CreateInstance(typeof(Plain), 3) in each overload, CreateInstanceFromArrayType(typeof(Plain[]), 3) and ArrayPool<Plain>.Shared.Rent(3) are too; none was reported. The two factories are reported when a typeof names the element (the element of the array type for the second); a Type in a variable could be anything and stays silent. Rent is recognised on ArrayPool<T> or an override of it, its T the element. The lengths are read from every int or long parameter and the elements of a params or written-out array of them, never the lower bounds, so #227's rule holds: a constant zero in any dimension stays silent. Input class (AIS): an expression that creates an array of a no-default struct, by the API that creates it. Every Array.CreateInstance and CreateInstanceFromArrayType overload that takes a length, Rent on the shared pool, on ArrayPool<T>.Create() and on a derived pool, each with a zero-length control; a Type variable, GetType(), typeof(Plain?) and an array of another element silent. Rent's T and the typeof element run across TypeArgumentKinds (79 kinds; dynamic and string? take Rent alone, typeof refusing them): each reports exactly where GC.AllocateArray<K> does. No sibling beyond #229's CollectionsMarshal.SetCount, which 2.4.0 takes. Reverting the source fails the forms row and the three kinds that report. Seeding the zero check away fails the three zero rows, reading the lower bounds as lengths fails the forms row, and skipping an array of lengths fails the silent row. Closes #228 Defect-Class: AIS Found-By: RS Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the class's implementation Assignments.Collect recorded a property assignment only on a bare IInstanceReferenceOperation and added the referenced property. A class reaches its own explicit interface implementation only through a cast, ((IHasId)this).Id = id, whose Instance is a conversion over this and whose property is the interface's, not the candidate. So a member every constructor assigned was reported, failing a warnings-as-errors build. Collect now strips conversions down to this, for fields and properties alike, and maps an interface's property to the analysed type's implementation (FindImplementationForInterfaceMember). Tuple elements and ref/out arguments go through the same Collect. A local holding this is not followed: IHasId self = this; self.Id = id stays reported, as the README now says. Input class (AIS): the forms that assign a member in a constructor. Plain and this. (silent before and after), a cast of this to an interface the member implements explicitly, implicitly, to the second of two it implements and to a generic one, a cast to the class itself for a field, deconstruction through the cast, and out through the cast: all seven were reported, the same cause, and are silent now. Two constructors where one assigns through the cast still name the other, and a cast of another instance assigns nothing. Reverting the source fails both rows; mapping nothing (casts stripped, the interface's property kept) fails both as well. Closes #231 Defect-Class: AIS Found-By: CA Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mber is another method
The previous commit's catalogue, run over the other checks that find a
hand-written member by name and parameters, found the same cause in seven
of them: each read a method of any arity and any ref kind.
Silent, measured with the check reverted:
- IsHandWrittenToString took a generic ToString<TFormat>() for the seam.
The value object printed "ToStringOverloadedByArity { Value = a }", what
a record prints, and a closed set printed its type name. A base record's
was CMTK1011 as an unsealed ToString().
- DeclaresCompareTo took a generic or by-in CompareTo(TSelf) for the
ordering seam and marked the ordering [HandWrittenOrdering], which the EF
Core satellite refuses to order by, though the ordering was generated.
- ValueObjectContractAspect skipped re-implementing a deeper base's explicit
IEquatable<TBase> beside a generic Equals<T>(TBase?), so a
HashSet<TBase> held two value objects holding 42.
Refused (CMTK1011, CMTK1008), where the generated member sits beside it:
a generic or by-in CompareTo(object), a base record's generic CompareTo,
TryFrom<T>(int) or TryFrom(in int) and Revalidate<T>() (InheritedSeams), a
generic Equals(TSelf) or GetHashCode() (EqualityDeclaredByHand), a base
record's (InheritedEquality, CMTK1023), and a generic PrintMembers.
Each check now reads only a method without type parameters whose
parameters are all by value (IsByValueAndNotGeneric), as every generated
member but TryParse and TryFormat is; those two are MethodsWithSignature's.
Input class (GTS): as in the previous commit, over the seams (ToString(),
CompareTo(TSelf), TryFrom, Revalidate) and what a record synthesizes or
calls (Equals, GetHashCode, PrintMembers, CompareTo(object)), on the type,
a base record, two levels up and in a closed set. 19 rows join
OverloadTests' 34, 4 the hand-written ones, and 3 check the ordering is
not marked hand-written. Reverting the commit refuses 5 of its types;
reverting IsHandWrittenToString alone fails the 3 ToString rows,
DeclaresCompareTo alone the 2 ordering rows, and the contract aspect's
check alone the deeper-base row.
Refs #226
Defect-Class: GTS
Found-By: VA
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…signing this one
Collect took any IInstanceReferenceOperation as this. An object
initializer's receiver and a with expression's are instance references
too, of the object they create. So a constructor whose only assignment
was _ = new Twin(0) { Id = x } counted as assigning its own Id, and the
member nothing set on this instance was not reported. Only the
containing type's instance counts now.
Found running #231's input class (the forms that assign a member in a
constructor) across what the receiver is: this, a cast of it, another
instance, a local alias, an object initializer and a with. The last two
were silent; both report now.
Reverting the source fails the row: 0 reports where 2 are expected.
Refs #231
Defect-Class: AIS
Found-By: VA
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…patch property
ASP.NET leaves out items and additionalProperties for a converter-backed
element, and PatchSchemaTransformer acted only on a Patch<T> itself. So a
Dictionary<string, Patch<string>> was {"type":"object"} and a
List<Patch<int>> {"type":"array"}, admitting anything, in 3.1, 3.0 and
2.0, while a Patch<T> property was its argument's schema admitting null.
The container branch now puts the patch's own schema back where ASP.NET
left it out, asked for through the context, so it is filled as a property
is: a described or replaced argument, a value object through its wrapped
type, 3.0's nullable form.
Input class: where a Patch<T> sits. Catalogue: PatchSchemaTests'
arguments (12 kinds, now 14 with Lines[] and Dictionary<string, int>, a
list of a component and a dictionary) in 6 positions (property, list,
array, Dictionary<string,>, IReadOnlyDictionary<string,>, a list in a
dictionary) plus a list as the whole body, in a minimal API and an MVC
controller, over OpenAPI 3.1, 3.0 and 2.0 and the three value-object
placements (components, InlineValueObjects, inlined by the host). A host
that inlines everything has no polymorphic argument: ASP.NET refuses a
plain property of one too. A wire test reads a map and a list of patches
as the document says they are read. The nullable patch the catalogue
found is the next commit.
Reverted per verify-the-guard: the 11 new document rows fail; the
dictionary arm alone fails them too.
Closes #232
Defect-Class: FCI
Found-By: CA
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md # test/oracles/claims.json
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…assignment-silent-2-3-1 fix: CMTK0006 counts an assignment through a cast of this
…ema-2-3-1 fix: describe a container of patches, and a nullable patch, as a patch property
…r-captured-ordering-2-3-1 # Conflicts: # CHANGELOG.md # design/knowledge/README.md
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
… pass to each rule's focused tests DroppedTaskVariantTests is one class of 1,838 rows, which xunit's default mode ran one after another. On CI it kept the test step going for two to seven minutes after every other project had finished (#245). The analyzer test project now runs with parallelMode "all" (xunit.runner.json), so every row of every theory is scheduled on its own. No test there shares state: each builds its own compilation. The four sweeps (DroppedTaskVariantTests, TypeArgumentConstraintKindTests, QueryDelegateConditionTests and TypeArgumentSiteRun, which TypeArgumentConstraintVariantTests runs) set VariantSweep.Behaviors, which skips the verifier's second run of each reporting case under '#pragma warning disable'. Whether a pragma silences a rule is the same for every shape, and the focused tests of each rule (IgnoredOutcomeAnalyzerTests, TypeArgumentConstraintAnalyzerTests, DefaultProducingCallAnalyzerTests) keep it. The verifier's generated-code pass already never ran: every analyzer configures generated-code analysis. Seeded twice, each reverted by its inverse edit: - CMTK0003 dropping the first element of an array's tasks: DroppedTaskVariantTests fails 2 rows under the new mode, so the sweep still catches its own defects. - CMTK0003 reporting a probe rule only below a leading '#pragma warning disable': IgnoredOutcomeAnalyzerTests fails 16 tests in the pragma pass, DroppedTaskVariantTests passes. The pass is now the focused tests'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rallel-2-3-1 test: run the analyzer sweeps' rows in parallel, and leave the pragma pass to each rule's focused tests
… SQL Server database once After #250, CI's Linux leg ends every run, and the EF Core project is half of it: its two query sweeps are one class each of 12,275 rows, which xunit's default mode ran one after another (#251). A row waits on SQLite, SQL Server or PostgreSQL more than it computes: at four threads the project took 55 s instead of 123 s for the same CPU. The project now runs with parallelMode "all" (xunit.runner.json). A row reads only what was seeded for its model, and each SQLite class opens its own database per test. SqlServerRoundTripTests opts out ([TestClass(DisableParallelization = true)]): each of its tests creates the same tables in one database. Seeded by removing the opt-out: 5 of its 7 tests fail. The whole suite found a race in the SQL Server fixture: DatabaseAsync's GetOrAdd ran its factory more than once for callers that arrived together, and the first rows of a sweep dropped and created typekit_queries under each other. Each database is now created once behind a Lazy, as the sweeps' own seeded databases are. SqlServerFixtureTests asks for one database from 32 callers at once; with the plain GetOrAdd restored it fails 3 runs of 3. The profile also answered the issue's other half: a CMTK0003 sweep row costs its own file, and reusing the compilation or compiling the declarations to metadata saves nothing, so DroppedTaskVariantTests is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e fixture's create-once On CI, parallel rows bought the EF Core project almost nothing (#251, #252's run): the database containers share the runner's four cores with the tests, so what looked like waiting on a local machine with spare cores became CPU. The Linux test step went from 9m 28s to 9m 17s, and the Windows one from 5m 17s to 7m 11s, as the projects beside EF Core slowed. xunit.runner.json and SqlServerRoundTripTests' opt-out go. SqlServer.DatabaseAsync keeps creating each database once behind a Lazy, with SqlServerFixtureTests as its guard: GetOrAdd may run its factory twice for callers that arrive together, which rows run in parallel did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-3-1 test: create each SQL Server test database once, as rows arriving together would race it
…ordering-2-3-1 fix: let a query filter order the context's own values without a lambda
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…orted-2-3-1 fix: CMTK0005 reports Array.CreateInstance, CreateInstanceFromArrayType and ArrayPool<T>.Rent
…-3-1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…load-refused-2-3-1 fix: keep an overload of a generated member that differs by arity or ref kind
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-refused-2-3-1 fix: a base's private member of a generated name is hidden from the value object, not refused
The 2.3.1 section is dated 2026-10-31 and links blob/v2.3.1/. Shipped.md moves its empty release header from 2.3.0 to 2.3.1, since Roslyn's release tracking refuses a header after an empty release (RS2007); 2.3.1 adds no rule. The comparison and the README's Status name 2.3.1, the TypeKit cells checked against the release branch on 2026-10-31, none changed. Closes #254 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs: close 2.3.1: the changelog dated, the analyzer release recorded
…ngelog main opened 2.4.0 after release/2.3.1 was cut from v2.3.0. The tag goes on the release PR's merge into main and is checked against Version, so both conflicts keep the release side; the tree is release/2.3.1's. 2.4.0 opens on main again after the tag, with the baseline at 2.3.1. Closes #256 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es-main build: merge main into release/2.3.1, keeping 2.3.1's version and changelog
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.
Closes #198
Release 2.3.1 into main
The changelog's 2.3.1 section, dated 2026-10-31, is the release notes: a patch of 2.3.0 with no new feature and no new public member. Generated members a value object or closed set reaches through a base record, a base class or an interface now fail with a CMTK error that names them, where they stood in silently or crashed Metalama; overloads that differ by arity or ref kind and a base's private members are kept. CMTK0005 and CMTK0006 cover more ways to make a default slot. EF Core refuses an ordering it would answer by the column, and builds a query filter it runs in memory.
Checked locally on
release/2.3.1at bce9e84 before the tag (CONTRIBUTING, "Releasing", step 1):oracles seeds: every seed caught, every baseline passing without its seed, the SQL Server oracles included (exit 0).docs-query due --nuget: no fact due (0 of 34 pinned external).The last topics were #244 (#230), #255 (the changelog dated, the analyzer release recorded) and #257 (
mainmerged in, keeping 2.3.1'sVersion, sincemainhad opened 2.4.0). The tag goes on this PR's merge commit; step 5 then opens 2.4.0 onmainagain with the baseline at 2.3.1.🤖 Generated with Claude Code