Skip to content

Releases: linkdotnet/IfItQuacks

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 18 Sep 08:03

Added

  • [DuckTyped] extension methods: the receiver is duck-typed too, so person.Greet() works for any type fitting the interface. Previously reported with IFITQUACKS004.

  • A public property or field of a delegate type satisfies an interface method of the same name, which makes an object literal a test double: Duck.As<IRepository>(new { Find = (Func<int, Order?>)(id => ...) }).

  • Duck.Stub<TShape>(value) and Duck.Stub<TShape>() return a test double that forwards the members the value provides and throws DuckStubException for the rest. A member that is present but doesn't fit is still reported with IFITQUACKS001.

  • Duck.Merge<TShape>(first, second) (and a three-value overload) builds one interface from several values, taking each member from the first value providing it - a spy or a partial fake without a mocking library.

  • Sequences are adapted element by element: IEnumerable<T>, IReadOnlyCollection<T> and IReadOnlyList<T> parameters accept a List<Person> or a Person[] whose elements fit, and Duck.As<IReadOnlyList<INamed>>(people) keeps Count and the indexer.

  • Duck.To<TTarget>(value) copies a value into a new one of a concrete type, filling the constructor and the remaining settable members from the members of the same name. Diagnostic IFITQUACKS009.

  • Samples: IfItQuacks.Sample.Extensions, IfItQuacks.Sample.Sequences, IfItQuacks.Sample.Testing and IfItQuacks.Sample.Copying.

Fixed

  • greeter?.Greet(duck) is intercepted. A conditional access used to fall through to the runtime fallback and throw DuckTypeMismatchException although the call was right there in the project.

  • base.Greet(duck) no longer silently calls the overriding method. A base call is not virtual, which neither an interceptor nor the fallback overload can reproduce, so it is reported with the new IFITQUACKS008.

  • An argument typed as Person? shares the adapter generated for Person instead of getting a second one whose generated code warned about a possible null dereference.

Changed

  • IFITQUACKS007 is titled Duck conversion target must be an interface and names the conversion (Duck.As, Duck.Stub or Duck.Merge) that was called.

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 19:06

Added

  • Delegates, lambdas and method groups satisfy an interface with a single method (a functional interface), both as [DuckTyped] arguments and through Duck.As.

  • A [DuckTyped] method converts to a delegate over the duck type (Func<Person, string> f = Ops.Describe;, items.Select(Ops.Describe)) through a generated overload.

  • static abstract members and operators via duck-typed constraints (where T : IAddable<T>), including BCL generic math interfaces such as IAdditionOperators<T, T, T>. The generated adapter is its own type argument, so these calls don't box.

  • Samples: IfItQuacks.Sample.Delegates and IfItQuacks.Sample.GenericMath.

  • Documentation: a Benchmarks page with measured numbers (BenchmarkDotNet, MemoryDiagnoser) for duck-typed calls and for Duck.As used as a view instead of a mapper.

  • Samples: IfItQuacks.Sample.Members (events, indexers, ref returns, default interface members) and IfItQuacks.Sample.Signatures (ref/out/params parameters, private methods, methods on a struct, runtime fallback).

Changed

  • Documentation: Known limitations is grouped into fewer sections, each showing the code that doesn't work.

Fixed

  • IFITQUACKS005 no longer mentions ref returns, which are supported now, and its title matches the documentation.
  • Interface properties and indexers with an init setter are adapted correctly. A type whose setter is init-only is reported as a mismatch instead of producing invalid code.
  • [DuckTyped] methods in an interface or in a file-local type are reported with IFITQUACKS004 instead of silently generating code for a different type. readonly and ref struct containing types keep their modifiers.
  • Members inherited from base interfaces are found when the argument is statically typed as an interface.
  • A member of the argument's type that hides the inherited member the interface was matched against no longer breaks the generated adapter.
  • Type argument inference skips static and generic candidate members and honours ref returns, matching the rules used for structural matching.

v1.1.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 17:38

Added

  • [DuckTyped] instance methods on structs. The receiver is passed by reference, so mutations reach the caller's value.
  • private, protected and private protected [DuckTyped] methods.
  • Interface members returning by ref or ref readonly (methods, properties and indexers) can be adapted if the matching member returns the same type by reference.

v1.0.0

Choose a tag to compare

@github-actions github-actions released this 17 Sep 16:37

Added

  • [DuckTyped] attribute for compile-time checked structural typing of interface parameters via generated adapters and interceptors. Any interface works, no attribute on the interface needed.
  • Diagnostics IFITQUACKS001 - IFITQUACKS006.
  • Generic interfaces and generic [DuckTyped] methods, with type arguments inferred per argument type.
  • Duck.As<TShape>(value) to convert a value explicitly, e.g. to store it in a field or collection. Diagnostic IFITQUACKS007.
  • [DuckTyped] instance methods and methods with several parameters, including multiple interface parameters, ref/out, params, default values and named arguments.
  • Interfaces with events, indexers and default interface members. Unsupported interface members are reported with IFITQUACKS005.
  • Framework interfaces like IDisposable and IEnumerable<T>; types implementing an interface (also explicitly or through variance) are passed through without an adapter.
  • Interface parameters accept null, default and omitted default values.
  • Calls that can't be verified at compile time (e.g. from generic code) work at runtime if the value implements the interface, otherwise DuckTypeMismatchException is thrown.
  • Generated types are internal, so several projects in a solution can use IfItQuacks.
  • Generated overloads are hidden from IntelliSense.
  • The generator is incremental and only re-analyzes calls to [DuckTyped] methods and Duck.As.
  • The package enables InterceptorsNamespaces for IfItQuacks.Generated, so no project file changes are needed.
  • Member types only need to be assignable (covariant returns and getters, contravariant parameters and setters, void interface methods discard results) instead of identical.
  • Public fields satisfy interface properties.
  • Anonymous types can be passed to [DuckTyped] methods and Duck.As.
  • Adapters forward Equals, GetHashCode and ToString to the wrapped instance; Duck.Unwrap returns it.