I thought this technique of eliminating dead code was called tree-shaking. And linking to be something entirely different: linking assemblies at (compile|run|analysis)-time.
Since reflection and dynamic invocation is a major feature of the runtime, they have avoided this optimization before.
Indeed. it's not that common, but I would expect it to happen. One example is that many IoC Containers allow you to scan assemblies and register all class + interface pairs that match given rules.
There has to be some safety-hatch to express "yes, I really want to keep that class and its methods. Don't strip them out".
This seems to be documented here: https://github.com/dotnet/core/blob/master/samples/linker-in...