From 8755bb8b335bab08bdc074b7d79dd3c82a93e7a1 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Mon, 22 Feb 2021 22:17:59 -0800 Subject: [PATCH 01/16] Proposal to add Framework specific inbox source generators This document describes desired characteristics for source generators we wish to add to .NET 6.0. --- accepted/2021/InboxSourceGenerators.md | 126 +++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 accepted/2021/InboxSourceGenerators.md diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md new file mode 100644 index 000000000..63a5a1625 --- /dev/null +++ b/accepted/2021/InboxSourceGenerators.md @@ -0,0 +1,126 @@ +# Inbox Source Generators + +**PM** [Immo Landwerth](https://github.com/terrajobst) | +**Dev** [Eric StJohn](https://github.com/ericstj) + +Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a users code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and static emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This techinque was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to a specific compoents within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. + +## Scenarios and User Experience + +A developer can use source generators for framework API without making any changes to their project. Creating a new project should be sufficient. + +A developer can use source generators for components in NuGet packages by referencing the NuGet package for that component. + +When a component ships both in a NuGet package and a framework, the developer has a consistent experience with the source generator as they do with the library itself. If the package referenced is newer then the framework the source generator from the package will be used as is already the case for the library in that package. Similarly if the framework is newer than the NuGet package the framework's source generator will be used as is already the case for the library in the framework. + +A developer can recieve updates to a source generator through normal channels which they already obtain updates today. + +A developer can update their version of Visual Studio or Dotnet SDK and have confidence that new versions of the toolchain won't impact their applciations which target older frameworks, outside of known and published servicing updates. + +A developer has confidence that their library using source generators will continue to work in the same way a library without source generators works on future versions of .NET following the same compatibility rules. + +## Requirements + +### Goals + +- Enable consumption of source generators for components in shared frameworks +- Address conflict resolution between source generators in shared frameworks and nuget packages +- Define servicing process for shared framework source generators +- Establish guidelines building and versioning shared framework source generators +- Describe compatibility requirements for source generators. + +### Non-Goals + +- Define a process for components not in shared frameworks to contribute source generators. +- Define any changes to the source generator architecture. +- Define any implementation details of source generators or source generator tests. +- Define a process around internal or non-shipping source generators + +## Stakeholders and Reviewers + +- ASP.NET - also building shared framework source generators. +- .NET Libraries team - building multiple shared framework source generators. +- Mono team - interested in providing source generators. +- Roslyn team - responsible for source generator infrastructure. +- .NET SDK team - responsible for resolving shared frameworks and doing conflict resolution with content. +- NuGet team - FYI only, no changes requested + +## Design + +### Framework provided source generators + +Framework provided source generators should have a mechanism to be exposed and versioned along side the framework and library which they extend. These source generators will be passed to the compiler when that framework is targeted. These source generators will not ne used when the framework is not targeted. For example, ASP.NET specific source generators will not be used with ASP.NET is not referenced. The version of the analyzers specific to the TargetFramework version will be used. For example, when targeting .NET 6.0 the analyzers specific to net6.0 will be used and not those for .NET 7.0, even when using the SDK that ships with .NET 7.0. + +To facilitate this, we will package analyzers inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [nuget conventions for describing analyzers](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). The .NET SDK will be responsible for locating the appropriate analyzers for the TargetFramework and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other Analyzer sources based as described in the following section. + +### Source generator conflict resolution + +Source generators and analyzers are passed to the compiler as @(Analyzer) items. These items may come from NuGet packages, be built into the SDK targets, be directly defined by the user via ProjectReference or raw Analyzer items, or be provided by the new framework mechanism described in this document. All should be considered for conflict resolution based on the same constraint. + +The .NET SDK already does conflict resolution for other asset types: references and copy-local runtime assets. It should do the same for Analyzers. Analyzers need not have "platform manifest" inputs for conflict resolution since all analyzers will be available during build, unlike runtime files which may not be available. Comparison for the sake of conflict resolution can consider the same assembly and file version rules as reference files. When finding two or more source generators with the same name + +Source generator conflict resolution should run before compile, but after all NuGet package assets have been evaluated. Specific sequencing, public/private target naming, extensibility will not be specified here but should follow best practice as determined by the SDK team. + +### Source generator versioning + +Source generators should be strongly named and update assembly version with every public stable release (GA and servicing). This ensures that the publicly shipped source generators can be loaded side-by-side in the same process and garuntees + +### Source generator compatibility concerns + +Source generators should only use public API in framework components they extend. This public API already needs to meet the strict binary compatibility garuntees as framework API. As such libraries containing source-generated code will be binary-compatible with future releases, just as if the user wrote this code directly. + +Source generators should not use public API with "reserved" or "undocumented" functionality in attempts to avoid public API garuntees. Libraries paired with source generators need to provide the same garuntees as libraries alone do today. + +Source generators should avoid emitting public API in user's projects. Such API can create a compatibility contract in that user's library which needs to be maintained. If a source generator produces public API in a user's project by design, that public API must be strictly maintained following the same rules as framework components across versions of the source generator. Care should be taken to the public API which is generated and should be reviewed by the Framework Design Council. A similar constraint applies to internal API: users will have source that expects to interact with the generated internal API and the user will expect that code to continue to compile between versions. + +Generated code should not be generated in System namespaces, it should prefer the user's namespace. Generated code should be triple-slashed commented and be free from compiler warnings. Generated code should avoid name colisions with user code and provide affordances for resolving name conflicts. + +### Functionality concerns for source generators + +Though the following are not specific to inbox source generators, these concerns are worth reviewing to describe how they apply to framework specific source generators. + +Source generators target .NETStandard and must run on both .NETFramework and .NETCore. + +Source generators should limit their dependencies. There is no scheme for encoding framework-specific nor runtime-specific implementations of dependencies or source generators based on the runtime environment so source generators should avoid any dependencies on assemblies which need to differ by framework or runtime. Source generators may depend on packages which are known dependencies of the compiler and should depend on versions less than or equal to that provided by the compiler and should not include that dependency in their package. + +Source generators should not directly reference nor execute runtime code. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. There is no type-equivalence garunteed between references of the source generator and those of the user code that is under analysis. As such source generators will need to examine user code references by name, or when needing to make equivalence checks can resolve types in the user code's type reference set to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library code. + +## Q & A + +### Why not just deliver source generators in the SDK same as roslyn analyzers? + +Doing so would require source generators to carefully version and test across all framework versions. This would increase testing cost for source generators and increase risk when shipping updates to source generators since they would apply to past, stable TargetFrameworks. Conceptually source generators are lifting a substantial amount of runtime functionality out of a library and executing that at design time. This code is directly included in the user's application and contributes to runtime behavior. + +### Inplace servicing updates are good enough for the compiler, why not source generators? + +The compiler has a very stable 1:1 relationship between a user's source and the code it generates. As such the user has the ability to control the output of the compiler through their own usage. The compiler's contribution of complexity to the user's assembly is very small and highly specified. + +Contrast this to source generators which can take a very small amount of user source to produce a large amount of generated code. This large amount of generated code contains significant complexity which is loosely specified. It's behavior is specific to the component it extends. It's likely that we'd need to bugfix this code over time, and change it as we develop features in the components it extends. It's also likely that we will learn new techniques and improvments to source generators over time, increasing the changes for bugfixes and features + +User's expect stability for projects which do not change, for example a project targeting an LTS release. It is much more difficult to ensure this stability when we need to significantly components which contribute complexity to that project. + +### Is it bad to have multiple versions of a source generator possibly loaded by a repository? + +A repository may have projects targeting multiple frameworks. These frameworks may contain different versions of the same source generator. When building the repository this may require the same compiler server instance to load multiple copies of a source generator. The same compiler would not likely load multiple copies of analyzers which do not ship side by side. + +### Should all inbox source generators be tied to a shared framework? + +No, some source generators might be independent of libraries in a shared framework: these could go in a non-side-by-side location (eg: analyzers folder). Other source generators may not contain functionality, but may instead just build some convenience API on top of existing public API. These could be considered stable enough to place in a non-side-by-side location. + +### Should only source generators be shipped this way? What about analyzers and code-fixes? + +Nothing is preventing analyzers and code fixes from shipping in the same manner. If it's desireable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. + +### Why in the ref pack, what not some other "analyzer" pack? + +The SDK already has a means for acquisition and selection of ref packs. The inputs to the selection of ref packs are the same as that of source generators (Version, FrameworkReference, and TargetFramework). Ref packs user normal nuget convention which support analyzers and source generators. + +It may be desireable to create a seperate package just for analyzers in order to reduce risk in servicing. We support servicing ref packs today, but we have to be careful not to expose new API nor change any assembly identites. + +### Why in the ref pack, what not runtime pack? + +Runtime packs are currently independent of compile. Sourcing inputs for compile from a runtime-specific package would break this and require signicant changes to the user experience, such as permitting cross-compiling by runtime or "targeting" a runtime. + +### I really really need to execute runtime code in the TargetFramework! + +You probably need a designer or indepent build component that has a host process that can leverage the runtime targeting information of the application. At the moment this doesn't have a great pattern to follow with source generators. The compiler does not provide a host, nor does it know anything about target runtime (or in some cases executable frameworks). \ No newline at end of file From 06b6430793c3b907bc8df5e5e8dd3475fd28ac89 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 23 Feb 2021 00:08:34 -0800 Subject: [PATCH 02/16] Apply suggestions from code review Co-authored-by: Jan Kotas --- accepted/2021/InboxSourceGenerators.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 63a5a1625..3da9f5781 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -49,7 +49,7 @@ A developer has confidence that their library using source generators will conti ### Framework provided source generators -Framework provided source generators should have a mechanism to be exposed and versioned along side the framework and library which they extend. These source generators will be passed to the compiler when that framework is targeted. These source generators will not ne used when the framework is not targeted. For example, ASP.NET specific source generators will not be used with ASP.NET is not referenced. The version of the analyzers specific to the TargetFramework version will be used. For example, when targeting .NET 6.0 the analyzers specific to net6.0 will be used and not those for .NET 7.0, even when using the SDK that ships with .NET 7.0. +Framework provided source generators should have a mechanism to be exposed and versioned along side the framework and library which they extend. These source generators will be passed to the compiler when that framework is targeted. These source generators will not ne used when the framework is not targeted. For example, ASP.NET specific source generators will not be used when ASP.NET is not referenced. The version of the analyzers specific to the TargetFramework version will be used. For example, when targeting .NET 6.0 the analyzers specific to net6.0 will be used and not those for .NET 7.0, even when using the SDK that ships with .NET 7.0. To facilitate this, we will package analyzers inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [nuget conventions for describing analyzers](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). The .NET SDK will be responsible for locating the appropriate analyzers for the TargetFramework and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other Analyzer sources based as described in the following section. @@ -67,9 +67,9 @@ Source generators should be strongly named and update assembly version with ever ### Source generator compatibility concerns -Source generators should only use public API in framework components they extend. This public API already needs to meet the strict binary compatibility garuntees as framework API. As such libraries containing source-generated code will be binary-compatible with future releases, just as if the user wrote this code directly. +Source generators should only use public API in framework components they extend. This public API already needs to meet the strict binary compatibility guarantees as framework API. As such libraries containing source-generated code will be binary-compatible with future releases, just as if the user wrote this code directly. -Source generators should not use public API with "reserved" or "undocumented" functionality in attempts to avoid public API garuntees. Libraries paired with source generators need to provide the same garuntees as libraries alone do today. +Source generators should not use public API with "reserved" or "undocumented" functionality in attempts to avoid public API guarantees. Libraries paired with source generators need to provide the same guarantees as libraries alone do today. Source generators should avoid emitting public API in user's projects. Such API can create a compatibility contract in that user's library which needs to be maintained. If a source generator produces public API in a user's project by design, that public API must be strictly maintained following the same rules as framework components across versions of the source generator. Care should be taken to the public API which is generated and should be reviewed by the Framework Design Council. A similar constraint applies to internal API: users will have source that expects to interact with the generated internal API and the user will expect that code to continue to compile between versions. @@ -83,7 +83,7 @@ Source generators target .NETStandard and must run on both .NETFramework and .NE Source generators should limit their dependencies. There is no scheme for encoding framework-specific nor runtime-specific implementations of dependencies or source generators based on the runtime environment so source generators should avoid any dependencies on assemblies which need to differ by framework or runtime. Source generators may depend on packages which are known dependencies of the compiler and should depend on versions less than or equal to that provided by the compiler and should not include that dependency in their package. -Source generators should not directly reference nor execute runtime code. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. There is no type-equivalence garunteed between references of the source generator and those of the user code that is under analysis. As such source generators will need to examine user code references by name, or when needing to make equivalence checks can resolve types in the user code's type reference set to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library code. +Source generators should not directly reference nor execute runtime code. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. There is no type-equivalence guaranteed between references of the source generator and those of the user code that is under analysis. As such source generators will need to examine user code references by name, or when needing to make equivalence checks can resolve types in the user code's type reference set to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library code. ## Q & A @@ -115,7 +115,7 @@ Nothing is preventing analyzers and code fixes from shipping in the same manner. The SDK already has a means for acquisition and selection of ref packs. The inputs to the selection of ref packs are the same as that of source generators (Version, FrameworkReference, and TargetFramework). Ref packs user normal nuget convention which support analyzers and source generators. -It may be desireable to create a seperate package just for analyzers in order to reduce risk in servicing. We support servicing ref packs today, but we have to be careful not to expose new API nor change any assembly identites. +It may be desirable to create a separate package just for analyzers in order to reduce risk in servicing. We support servicing ref packs today, but we have to be careful not to expose new API nor change any assembly identities. ### Why in the ref pack, what not runtime pack? @@ -123,4 +123,4 @@ Runtime packs are currently independent of compile. Sourcing inputs for compile ### I really really need to execute runtime code in the TargetFramework! -You probably need a designer or indepent build component that has a host process that can leverage the runtime targeting information of the application. At the moment this doesn't have a great pattern to follow with source generators. The compiler does not provide a host, nor does it know anything about target runtime (or in some cases executable frameworks). \ No newline at end of file +You probably need a designer or indepent build component that has a host process that can leverage the runtime targeting information of the application. At the moment this doesn't have a great pattern to follow with source generators. The compiler does not provide a host, nor does it know anything about target runtime (or in some cases executable frameworks). From 58a70dcd9dced3dbd20393dbcb4449ad9fcc63fc Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 23 Feb 2021 00:09:23 -0800 Subject: [PATCH 03/16] Apply suggestions from code review Co-authored-by: Jan Kotas --- accepted/2021/InboxSourceGenerators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 3da9f5781..aae92aca9 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -3,7 +3,7 @@ **PM** [Immo Landwerth](https://github.com/terrajobst) | **Dev** [Eric StJohn](https://github.com/ericstj) -Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a users code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and static emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This techinque was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to a specific compoents within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. +Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a users code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and static emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This techinque was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to a specific components within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. ## Scenarios and User Experience @@ -15,7 +15,7 @@ When a component ships both in a NuGet package and a framework, the developer ha A developer can recieve updates to a source generator through normal channels which they already obtain updates today. -A developer can update their version of Visual Studio or Dotnet SDK and have confidence that new versions of the toolchain won't impact their applciations which target older frameworks, outside of known and published servicing updates. +A developer can update their version of Visual Studio or Dotnet SDK and have confidence that new versions of the toolchain won't impact their applications which target older frameworks, outside of known and published servicing updates. A developer has confidence that their library using source generators will continue to work in the same way a library without source generators works on future versions of .NET following the same compatibility rules. From f477d8498c066fd70325ce04baf301be3ac90bf8 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 23 Feb 2021 16:29:17 -0800 Subject: [PATCH 04/16] Respond to feedback. --- accepted/2021/InboxSourceGenerators.md | 87 +++++++++++++++++++------- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index aae92aca9..aa23dc7fa 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -3,7 +3,7 @@ **PM** [Immo Landwerth](https://github.com/terrajobst) | **Dev** [Eric StJohn](https://github.com/ericstj) -Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a users code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and static emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This techinque was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to a specific components within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. +Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a user's code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and static emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This technique was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to a specific components within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. ## Scenarios and User Experience @@ -11,20 +11,20 @@ A developer can use source generators for framework API without making any chang A developer can use source generators for components in NuGet packages by referencing the NuGet package for that component. -When a component ships both in a NuGet package and a framework, the developer has a consistent experience with the source generator as they do with the library itself. If the package referenced is newer then the framework the source generator from the package will be used as is already the case for the library in that package. Similarly if the framework is newer than the NuGet package the framework's source generator will be used as is already the case for the library in the framework. +When a component ships both in a NuGet package and a framework, the developer has a consistent experience with the source generator as they do with the library itself. If the package referenced is newer than the framework the source generator from the package will be used as is already the case for the library in that package. Similarly if the framework is newer than the NuGet package the framework's source generator will be used as is already the case for the library in the framework. -A developer can recieve updates to a source generator through normal channels which they already obtain updates today. +A developer can receive updates to a source generator through normal channels which they already obtain updates today. A developer can update their version of Visual Studio or Dotnet SDK and have confidence that new versions of the toolchain won't impact their applications which target older frameworks, outside of known and published servicing updates. -A developer has confidence that their library using source generators will continue to work in the same way a library without source generators works on future versions of .NET following the same compatibility rules. +A developer has confidence that their library using inbox source generators will continue to work in the same way a library that doesn't use inbox source generators works on future versions of .NET. Source gener ## Requirements ### Goals - Enable consumption of source generators for components in shared frameworks -- Address conflict resolution between source generators in shared frameworks and nuget packages +- Address conflict resolution between source generators in shared frameworks and NuGet packages - Define servicing process for shared framework source generators - Establish guidelines building and versioning shared framework source generators - Describe compatibility requirements for source generators. @@ -41,6 +41,7 @@ A developer has confidence that their library using source generators will conti - ASP.NET - also building shared framework source generators. - .NET Libraries team - building multiple shared framework source generators. - Mono team - interested in providing source generators. +- .NET Runtime team - interested in providing source generators. - Roslyn team - responsible for source generator infrastructure. - .NET SDK team - responsible for resolving shared frameworks and doing conflict resolution with content. - NuGet team - FYI only, no changes requested @@ -49,37 +50,67 @@ A developer has confidence that their library using source generators will conti ### Framework provided source generators -Framework provided source generators should have a mechanism to be exposed and versioned along side the framework and library which they extend. These source generators will be passed to the compiler when that framework is targeted. These source generators will not ne used when the framework is not targeted. For example, ASP.NET specific source generators will not be used when ASP.NET is not referenced. The version of the analyzers specific to the TargetFramework version will be used. For example, when targeting .NET 6.0 the analyzers specific to net6.0 will be used and not those for .NET 7.0, even when using the SDK that ships with .NET 7.0. +Framework provided source generators should have a mechanism to be exposed and versioned along side the framework and library which they extend. These source generators will be passed to the compiler when that framework is targeted. These source generators will not be used when the framework is not targeted. For example, ASP.NET specific source generators will not be used when ASP.NET is not referenced. The version of the source generators specific to the TargetFramework version will be used. For example, when targeting .NET 6.0 the source generators specific to net6.0 will be used and not those for .NET 7.0, even when using the SDK that ships with .NET 7.0. -To facilitate this, we will package analyzers inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [nuget conventions for describing analyzers](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). The .NET SDK will be responsible for locating the appropriate analyzers for the TargetFramework and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other Analyzer sources based as described in the following section. +Source generators, analyzers, and code fixes are all represented and communicated in the same manner to the compiler, as analyzer dlls using the `-analyzer` switch. Though this document is motivated by describing the process for source generators it will work in the same manner for analyzers and in some places we may refer to the assemblies containing source generators as analyzers due to the way they are understood by the compiler, package manager, and SDK. + +To facilitate this, we will package source generators inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [NuGet conventions for describing analyzers](https://docs.microsoft.com/en-us/nuget/guides/analyzers-conventions) with respect to the location in the package, however the SDK will not probe this path. All source generators and their metadata will be listed in FrameworkList.xml, similar to references. Analyzers will be added as `File` elements with `Type="Analyzer"` (constrasting to `Type="Managed"` which is currently used for references) and optionally `Language="cs|vb"`. Omitting the `Language` attribute means the analyzer applies to all languages. The .NET SDK will be responsible for locating the appropriate source generators from the refpack, based on the project's `$(Language)` and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other `@(Analyzer)` sources as described in the following section. When viewed from the IDE it should be clear that the source of the `Analyzer` is the framework reference, similarly to how it's clear that the source of NuGet package analyzers come from packaages. + +Alternative designs are listed in the [Q & A](#Q%20&%20A) section below. ### Source generator conflict resolution Source generators and analyzers are passed to the compiler as @(Analyzer) items. These items may come from NuGet packages, be built into the SDK targets, be directly defined by the user via ProjectReference or raw Analyzer items, or be provided by the new framework mechanism described in this document. All should be considered for conflict resolution based on the same constraint. -The .NET SDK already does conflict resolution for other asset types: references and copy-local runtime assets. It should do the same for Analyzers. Analyzers need not have "platform manifest" inputs for conflict resolution since all analyzers will be available during build, unlike runtime files which may not be available. Comparison for the sake of conflict resolution can consider the same assembly and file version rules as reference files. When finding two or more source generators with the same name +The .NET SDK already does conflict resolution for other asset types: references and copy-local runtime assets. It should do the same for source generators, ore more generally Analyzers. Analyzers need not have "platform manifest" inputs for conflict resolution since all analyzers will be available during build, unlike runtime files which may not be available. Comparison for the sake of conflict resolution can consider the same assembly and file version rules as reference files. When finding two or more source generators with the same name conflict resolution will select the generator with higher assembly version, then higher file version, then that which is in the framework. This parallels the same rules followed by reference assemblies. The SDK may choose to optimize this comparison as it does for reference assemblies, by including a data file that includes analyzer metadata. Source generator conflict resolution should run before compile, but after all NuGet package assets have been evaluated. Specific sequencing, public/private target naming, extensibility will not be specified here but should follow best practice as determined by the SDK team. ### Source generator versioning -Source generators should be strongly named and update assembly version with every public stable release (GA and servicing). This ensures that the publicly shipped source generators can be loaded side-by-side in the same process and garuntees +Source generators should be strongly named and update assembly version with every public stable release (GA and servicing). This ensures that the publicly shipped source generators can be loaded side-by-side in the same process and follows our versioning guidelines for components which ship and must run on .NETFramework. Source generators need not update assembly version during pre-release but should update file version to facilitate well behaved distribution by installers. In general source generators need to follow all the same guidelines as other code that ships out of dotnet/runtime and runs outside the shared framework. ### Source generator compatibility concerns -Source generators should only use public API in framework components they extend. This public API already needs to meet the strict binary compatibility guarantees as framework API. As such libraries containing source-generated code will be binary-compatible with future releases, just as if the user wrote this code directly. +Source generators should only generate code which uses public API in framework components they extend. This public API already needs to meet the strict binary compatibility guarantees as framework API. As such libraries containing source-generated code will be binary-compatible with future releases, just as if the user wrote this code directly. + +Source generators should not generate code which uses public API with "reserved" or "undocumented" functionality in attempts to avoid public API guarantees. Libraries paired with source generators need to provide the same guarantees as libraries alone do today. + +Source generators should avoid emitting public API in a user's projects. Such API can create a compatibility contract in that user's library which needs to be maintained. If a source generator produces public API in a user's project by design, that public API must be strictly maintained following the same rules as framework components across versions of the source generator. Care should be taken to the public API which is generated and should be reviewed by the Framework Design Council. A similar constraint applies to internal API: users will have source that expects to interact with the generated internal API and the user will expect that code to continue to compile between versions. + +Generated code should not be generated in System namespaces, it should prefer the user's namespace. Generated code should be triple-slashed commented and be free from compiler warnings. Generated code should avoid name collisions with user code and provide affordances for resolving name conflicts. As much as possible generated code should meet the quality statndards of .NET library code, using nullable annotations meeting framework design and naming guidelines, etc. It should feel like a natural extension of the library. + +### Source generator servicing + +Source generators contribute significant code to user assemblies and that code may have issues. We have very little control over changing customer assemblies once they have consumed a source generator. We cannot and should not attempt to change the behavior of customer's assemblies (eg: hot patch) to fix issues. Should an issue be found we should fix it in the source generator and ship a new version, just as we would if we found an issue with compiler. Customer's desiring this fix must proactively acquire the new code-generator, typically through an updated SDK which carries the ref pack, rebuild their code, and update their production applications. + +Due to the limited servicing agility for code-generated code, we should be very careful about the complexity of the code which is generate. Where possible we should limit code-generated code to "glue code", only that which must be specific to the user assembly or types. We should try to put more complex code inside the framework or library itself, even if it means exposing public API that is specifically for source generated code to call. -Source generators should not use public API with "reserved" or "undocumented" functionality in attempts to avoid public API guarantees. Libraries paired with source generators need to provide the same guarantees as libraries alone do today. +**Open issue:** should we do anything to make it easier to identify assemblies which might need an update? We can consider some case studies. -Source generators should avoid emitting public API in user's projects. Such API can create a compatibility contract in that user's library which needs to be maintained. If a source generator produces public API in a user's project by design, that public API must be strictly maintained following the same rules as framework components across versions of the source generator. Care should be taken to the public API which is generated and should be reviewed by the Framework Design Council. A similar constraint applies to internal API: users will have source that expects to interact with the generated internal API and the user will expect that code to continue to compile between versions. +If a specific compiler version has a code-gen bug it isn't clear from the final assembly if that bug is present, unless you directly inspect the IL/bytecode for the issue. The compiler version is embedded in the PDB of the assembly which could be used to identify assemblies with the issue. There is no notification at design time or runtime of any issue with the compiler, the user needs to subscribe to updates to understand when an update is needed. -Generated code should not be generated in System namespaces, it should prefer the user's namespace. Generated code should be triple-slashed commented and be free from compiler warnings. Generated code should avoid name colisions with user code and provide affordances for resolving name conflicts. +If a runtime assembly has a bug we have multiple avenues of detection. We could examine the assembly metadata files contained in the application layout, including for self-contained applications. The runtime will also write "breadcrumb" files on a machine when code is executed that is annotated for servicing that help identify that a specific assembly or package version has been executed on that machine. Today we use these breadcrumbs to help target updates to a machine. Those updates can install in a well known location and be preferred over the application's copy. + +So for means of detection we have: +1. Manual +2. PDB indicator: eg source generator assembly version, file version, hash) +3. Assembly metadata in consuming user project: eg AssemblyMetadata("SourceGeneratedBy", "source generator assembly version, file version, hash") +4. Runtime breadcrumbs + +For means of update we have: +1. No notification, user needs to subscribe to servicing updates. +2. Design time notification. +3. Killbit in the runtime to prevent execution of user assemblies that contain vulnerable source generated code. +4. ~~Automatically update user assembly though a hotpatch applied by runtime or JIT~~ + +We'll need to better understand what customer's expectations are around servicing to decide if we should do any work on these axes. My initial inclination is to only have a PDB indicator and require users to subscribe to updates. ### Functionality concerns for source generators Though the following are not specific to inbox source generators, these concerns are worth reviewing to describe how they apply to framework specific source generators. -Source generators target .NETStandard and must run on both .NETFramework and .NETCore. +Source generator assemblies should target .NETStandard and must run on both .NETFramework and .NETCore. Source generators should support generating source for all TargetFrameworks supported by their companion library. In the case of inbox source generators discussed in this document this need only be the TargetFramework the generator ships in. In the case of a NuGet package provided source generator, this should be all frameworks supported by package. Source generators should limit their dependencies. There is no scheme for encoding framework-specific nor runtime-specific implementations of dependencies or source generators based on the runtime environment so source generators should avoid any dependencies on assemblies which need to differ by framework or runtime. Source generators may depend on packages which are known dependencies of the compiler and should depend on versions less than or equal to that provided by the compiler and should not include that dependency in their package. @@ -95,9 +126,9 @@ Doing so would require source generators to carefully version and test across al The compiler has a very stable 1:1 relationship between a user's source and the code it generates. As such the user has the ability to control the output of the compiler through their own usage. The compiler's contribution of complexity to the user's assembly is very small and highly specified. -Contrast this to source generators which can take a very small amount of user source to produce a large amount of generated code. This large amount of generated code contains significant complexity which is loosely specified. It's behavior is specific to the component it extends. It's likely that we'd need to bugfix this code over time, and change it as we develop features in the components it extends. It's also likely that we will learn new techniques and improvments to source generators over time, increasing the changes for bugfixes and features +Contrast this to source generators which can take a very small amount of user source to produce a large amount of generated code. This large amount of generated code contains significant complexity which is loosely specified. Its behavior is specific to the component it extends. It's likely that we'd need to bugfix this code over time, and change it as we develop features in the components it extends. It's also likely that we will learn new techniques and improvments to source generators over time, increasing the changes for bugfixes and features. -User's expect stability for projects which do not change, for example a project targeting an LTS release. It is much more difficult to ensure this stability when we need to significantly components which contribute complexity to that project. +Users expect stability for projects which do not change, for example a project targeting an LTS release. It is much more difficult to ensure this stability when we need to significantly components which contribute complexity to that project. ### Is it bad to have multiple versions of a source generator possibly loaded by a repository? @@ -109,18 +140,26 @@ No, some source generators might be independent of libraries in a shared framewo ### Should only source generators be shipped this way? What about analyzers and code-fixes? -Nothing is preventing analyzers and code fixes from shipping in the same manner. If it's desireable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. +Nothing is preventing analyzers and code fixes from shipping in the same manner. If it's desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. + +### Why in the ref pack, why not some other "analyzer" pack? + +The SDK already has a means for acquisition and selection of ref packs. The inputs to the selection of ref packs are the same as that of source generators (Version, FrameworkReference, and TargetFramework), and the outputs are consumed by the same phase (compile) with the same level of cross-targeting granularity (TargetFramework and references). + +It may be desirable to create a separate package just for analyzers in order to reduce risk in servicing. We support servicing ref packs today, but we have to be careful not to expose new API nor change any assembly identities. In my opinion it makes more sense to invest efforts in ensuring safe servicing of ref packs, with potential increased complexity in our own builds. There is benefit in having servicable ref packs regardless and this keeps additional complexity (resolution of additional pack) out of user builds. + +### Why in the ref pack, why not runtime pack? -### Why in the ref pack, what not some other "analyzer" pack? +Runtime packs are currently independent of compile. Sourcing inputs for compile from a runtime-specific package would break this and require significant changes to the user experience, such as permitting cross-compiling by runtime or "targeting" a runtime. -The SDK already has a means for acquisition and selection of ref packs. The inputs to the selection of ref packs are the same as that of source generators (Version, FrameworkReference, and TargetFramework). Ref packs user normal nuget convention which support analyzers and source generators. +### Why in the ref pack, why not a dedicated NuGet package? -It may be desirable to create a separate package just for analyzers in order to reduce risk in servicing. We support servicing ref packs today, but we have to be careful not to expose new API nor change any assembly identities. +It's a goal for source-generators to be part of the default experience. Additionally the default experience should not require a restore step. As such analyzers cannot be delivered by NuGet packages alone. -### Why in the ref pack, what not runtime pack? +NuGet packages are a good option in addition to the ref pack for components which already ship as NuGet packages, eg System.Text.Json. In this way the source generator can deliver the integrated experience of being *part of* the library. -Runtime packs are currently independent of compile. Sourcing inputs for compile from a runtime-specific package would break this and require signicant changes to the user experience, such as permitting cross-compiling by runtime or "targeting" a runtime. +Standalone source-generator NuGet packages may be used for components which are in the shared framework when the source-generator is not ready to stabalize at the same time as the shared framework, and the component inside the shared framework does not need to be changed. If the component in the shared framework needs to be changed, it should be done so by either a library+generator NuGet package (eg: System.Text.Json) or an experimental build of the entire framework, typically from a codebase like https://github.com/dotnet/runtimelab. -### I really really need to execute runtime code in the TargetFramework! +### I really really need to execute runtime code on the TargetFramework! -You probably need a designer or indepent build component that has a host process that can leverage the runtime targeting information of the application. At the moment this doesn't have a great pattern to follow with source generators. The compiler does not provide a host, nor does it know anything about target runtime (or in some cases executable frameworks). +You probably need a designer or independent build component that has a host process that can leverage the runtime targeting information of the application. At the moment this doesn't have a great pattern to follow with source generators. The compiler does not provide a host, nor does it know anything about target runtime (or in some cases executable frameworks). From 8d646c5d2db0b38aa544ce1cdbf52ae3b5b8a214 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Wed, 24 Feb 2021 11:18:27 -0800 Subject: [PATCH 05/16] Address feedback --- accepted/2021/InboxSourceGenerators.md | 46 +++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index aa23dc7fa..3ced1343b 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -3,7 +3,7 @@ **PM** [Immo Landwerth](https://github.com/terrajobst) | **Dev** [Eric StJohn](https://github.com/ericstj) -Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a user's code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and static emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This technique was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to a specific components within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. +Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a user's code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and static emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This technique was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to specific components within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. ## Scenarios and User Experience @@ -11,13 +11,13 @@ A developer can use source generators for framework API without making any chang A developer can use source generators for components in NuGet packages by referencing the NuGet package for that component. -When a component ships both in a NuGet package and a framework, the developer has a consistent experience with the source generator as they do with the library itself. If the package referenced is newer than the framework the source generator from the package will be used as is already the case for the library in that package. Similarly if the framework is newer than the NuGet package the framework's source generator will be used as is already the case for the library in the framework. +When a component ships both in a NuGet package and a framework, the developer has a consistent experience with the source generator as they do with the library itself. If the package referenced is newer than the framework the source generator from the package will be used as is already the case for the library in that package. Similarly, if the framework is newer than the NuGet package the framework's source generator will be used as is already the case for the library in the framework. A developer can receive updates to a source generator through normal channels which they already obtain updates today. -A developer can update their version of Visual Studio or Dotnet SDK and have confidence that new versions of the toolchain won't impact their applications which target older frameworks, outside of known and published servicing updates. +A developer can update their version of Visual Studio or Dotnet SDK and have confidence that new versions of the toolchain will not impact their applications which target older frameworks, outside of known and published servicing updates. -A developer has confidence that their library using inbox source generators will continue to work in the same way a library that doesn't use inbox source generators works on future versions of .NET. Source gener +A developer has confidence that their library using inbox source generators will continue to work in the same way a library that does not use inbox source generators works on future versions of .NET. Source gener ## Requirements @@ -52,9 +52,9 @@ A developer has confidence that their library using inbox source generators will Framework provided source generators should have a mechanism to be exposed and versioned along side the framework and library which they extend. These source generators will be passed to the compiler when that framework is targeted. These source generators will not be used when the framework is not targeted. For example, ASP.NET specific source generators will not be used when ASP.NET is not referenced. The version of the source generators specific to the TargetFramework version will be used. For example, when targeting .NET 6.0 the source generators specific to net6.0 will be used and not those for .NET 7.0, even when using the SDK that ships with .NET 7.0. -Source generators, analyzers, and code fixes are all represented and communicated in the same manner to the compiler, as analyzer dlls using the `-analyzer` switch. Though this document is motivated by describing the process for source generators it will work in the same manner for analyzers and in some places we may refer to the assemblies containing source generators as analyzers due to the way they are understood by the compiler, package manager, and SDK. +Source generators, analyzers, and code fixes are all represented and communicated in the same manner to the compiler, as analyzer dlls using the `-analyzer` switch. Though this document is motivated by describing the process for source generators it will work in the same manner for analyzers. In some places we may refer to the assemblies containing source generators as analyzers due to the way they are understood by the compiler, package manager, and SDK. -To facilitate this, we will package source generators inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [NuGet conventions for describing analyzers](https://docs.microsoft.com/en-us/nuget/guides/analyzers-conventions) with respect to the location in the package, however the SDK will not probe this path. All source generators and their metadata will be listed in FrameworkList.xml, similar to references. Analyzers will be added as `File` elements with `Type="Analyzer"` (constrasting to `Type="Managed"` which is currently used for references) and optionally `Language="cs|vb"`. Omitting the `Language` attribute means the analyzer applies to all languages. The .NET SDK will be responsible for locating the appropriate source generators from the refpack, based on the project's `$(Language)` and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other `@(Analyzer)` sources as described in the following section. When viewed from the IDE it should be clear that the source of the `Analyzer` is the framework reference, similarly to how it's clear that the source of NuGet package analyzers come from packaages. +To facilitate this, we will package source generators inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [NuGet conventions for describing analyzers](https://docs.microsoft.com/en-us/nuget/guides/analyzers-conventions) with respect to the location in the package, however the SDK will not probe this path. All source generators and their metadata will be listed in FrameworkList.xml, similar to references. Analyzers will be added as `File` elements with `Type="Analyzer"` (constrasting to `Type="Managed"` which is currently used for references) and optionally `Language="cs|vb"`. Omitting the `Language` attribute means the analyzer applies to all languages. The .NET SDK will be responsible for locating the appropriate source generators from the refpack, based on the project's `$(Language)` and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other `@(Analyzer)` sources as described in the following section. When viewed from the IDE it should be clear that the source of the `Analyzer` is the framework reference, similarly to how it is clear that the source of NuGet package analyzers come from packaages. Alternative designs are listed in the [Q & A](#Q%20&%20A) section below. @@ -78,24 +78,24 @@ Source generators should not generate code which uses public API with "reserved" Source generators should avoid emitting public API in a user's projects. Such API can create a compatibility contract in that user's library which needs to be maintained. If a source generator produces public API in a user's project by design, that public API must be strictly maintained following the same rules as framework components across versions of the source generator. Care should be taken to the public API which is generated and should be reviewed by the Framework Design Council. A similar constraint applies to internal API: users will have source that expects to interact with the generated internal API and the user will expect that code to continue to compile between versions. -Generated code should not be generated in System namespaces, it should prefer the user's namespace. Generated code should be triple-slashed commented and be free from compiler warnings. Generated code should avoid name collisions with user code and provide affordances for resolving name conflicts. As much as possible generated code should meet the quality statndards of .NET library code, using nullable annotations meeting framework design and naming guidelines, etc. It should feel like a natural extension of the library. +Generated code should not be generated in System namespaces, it should prefer the user's namespace. Generated code should be triple-slashed commented and be free from compiler warnings. Generated code should avoid name collisions with user code and provide affordances for resolving name conflicts. As much as possible generated code should meet the quality standards of .NET library code, using nullable annotations, meeting framework design guidelines, naming guidelines, etc. It should feel like a natural extension of the library. ### Source generator servicing -Source generators contribute significant code to user assemblies and that code may have issues. We have very little control over changing customer assemblies once they have consumed a source generator. We cannot and should not attempt to change the behavior of customer's assemblies (eg: hot patch) to fix issues. Should an issue be found we should fix it in the source generator and ship a new version, just as we would if we found an issue with compiler. Customer's desiring this fix must proactively acquire the new code-generator, typically through an updated SDK which carries the ref pack, rebuild their code, and update their production applications. +Source generators contribute significant code to user assemblies and that code may have issues. We have very little control over changing customer assemblies once they have consumed a source generator. We cannot and should not attempt to change the behavior of customer's assemblies (eg: hot patch) to fix issues. Should an issue be found we should fix it in the source generator and ship a new version, just as we would if we found an issue with compiler. Customers desiring this fix must proactively acquire the new code-generator, typically through an updated SDK which carries the ref pack, rebuild their code, and update their production applications. Due to the limited servicing agility for code-generated code, we should be very careful about the complexity of the code which is generate. Where possible we should limit code-generated code to "glue code", only that which must be specific to the user assembly or types. We should try to put more complex code inside the framework or library itself, even if it means exposing public API that is specifically for source generated code to call. **Open issue:** should we do anything to make it easier to identify assemblies which might need an update? We can consider some case studies. -If a specific compiler version has a code-gen bug it isn't clear from the final assembly if that bug is present, unless you directly inspect the IL/bytecode for the issue. The compiler version is embedded in the PDB of the assembly which could be used to identify assemblies with the issue. There is no notification at design time or runtime of any issue with the compiler, the user needs to subscribe to updates to understand when an update is needed. +If a specific compiler version has a code-gen bug it is not clear from the final assembly if that bug is present, unless you directly inspect the IL/bytecode for the issue. The compiler version is embedded in the PDB of the assembly which could be used to identify assemblies with the issue. There is no notification at design time or runtime of any issue with the compiler, the user needs to subscribe to updates to understand when an update is needed. -If a runtime assembly has a bug we have multiple avenues of detection. We could examine the assembly metadata files contained in the application layout, including for self-contained applications. The runtime will also write "breadcrumb" files on a machine when code is executed that is annotated for servicing that help identify that a specific assembly or package version has been executed on that machine. Today we use these breadcrumbs to help target updates to a machine. Those updates can install in a well known location and be preferred over the application's copy. +If a runtime assembly has a bug, we have multiple avenues of detection. We could examine the assembly metadata files contained in the application layout, including for self-contained applications. The runtime will also write "breadcrumb" files on a machine when code is executed that is annotated for servicing that help identify that a specific assembly or package version has been executed on that machine. Today we use these breadcrumbs to help target updates to a machine. Those updates can install in a well-known location and be preferred over the application's copy. -So for means of detection we have: +For means of detection we have: 1. Manual -2. PDB indicator: eg source generator assembly version, file version, hash) -3. Assembly metadata in consuming user project: eg AssemblyMetadata("SourceGeneratedBy", "source generator assembly version, file version, hash") +2. PDB indicator: source generator assembly version, file version, hash +3. Assembly metadata in consuming user project: GeneratedCodeAttribute("System.Foo.Generator", "5.0.0+cf258a14b70ad9069470a108f13765e0e5988f51") 4. Runtime breadcrumbs For means of update we have: @@ -104,17 +104,17 @@ For means of update we have: 3. Killbit in the runtime to prevent execution of user assemblies that contain vulnerable source generated code. 4. ~~Automatically update user assembly though a hotpatch applied by runtime or JIT~~ -We'll need to better understand what customer's expectations are around servicing to decide if we should do any work on these axes. My initial inclination is to only have a PDB indicator and require users to subscribe to updates. +We will need to better understand what customer's expectations are around servicing to decide if we should do any work on these axes. My initial inclination is to only have a PDB indicator and require users to subscribe to updates. ### Functionality concerns for source generators Though the following are not specific to inbox source generators, these concerns are worth reviewing to describe how they apply to framework specific source generators. -Source generator assemblies should target .NETStandard and must run on both .NETFramework and .NETCore. Source generators should support generating source for all TargetFrameworks supported by their companion library. In the case of inbox source generators discussed in this document this need only be the TargetFramework the generator ships in. In the case of a NuGet package provided source generator, this should be all frameworks supported by package. +Source generator assemblies execute in the compiler process and must target .NETStandard in order run on both .NETFramework and .NETCore. Source generators should support generating source for all TargetFrameworks supported by their companion library. In the case of inbox source generators discussed in this document this need only be the TargetFramework the generator ships in. In the case of a NuGet package provided source generator, this should be all frameworks supported by package. Source generators should limit their dependencies. There is no scheme for encoding framework-specific nor runtime-specific implementations of dependencies or source generators based on the runtime environment so source generators should avoid any dependencies on assemblies which need to differ by framework or runtime. Source generators may depend on packages which are known dependencies of the compiler and should depend on versions less than or equal to that provided by the compiler and should not include that dependency in their package. -Source generators should not directly reference nor execute runtime code. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. There is no type-equivalence guaranteed between references of the source generator and those of the user code that is under analysis. As such source generators will need to examine user code references by name, or when needing to make equivalence checks can resolve types in the user code's type reference set to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library code. +Source generators should not rely on referencing nor executing framework types they wish to extend. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. For example, API added to help a source generator do its job may not be present when running on .NETFramework or the .NETCore version the compiler is running on. There is no type-equivalence guaranteed between types loaded in the compiler process and those of the user code that is under analysis. For example, a source generator cannot instantiate attribute instances in the compiler process from user references to those attributes in source. Should a source generator need to identify type usage it will need to examine user code references by name or resolve types in the user's references to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library helper code when generating source. For example, serializer code which needs to examine the shape of user types can be shared between the runtime library and source generator. Common abstractions can be made over metadata to allow the same code to handle different sources of metadata (source vs reflection). ## Q & A @@ -124,9 +124,9 @@ Doing so would require source generators to carefully version and test across al ### Inplace servicing updates are good enough for the compiler, why not source generators? -The compiler has a very stable 1:1 relationship between a user's source and the code it generates. As such the user has the ability to control the output of the compiler through their own usage. The compiler's contribution of complexity to the user's assembly is very small and highly specified. +The compiler has a very stable 1:1 relationship between a user's source and the code it generates. As such the user can control the output of the compiler through their own usage. The compiler's contribution of complexity to the user's assembly is very small and highly specified. -Contrast this to source generators which can take a very small amount of user source to produce a large amount of generated code. This large amount of generated code contains significant complexity which is loosely specified. Its behavior is specific to the component it extends. It's likely that we'd need to bugfix this code over time, and change it as we develop features in the components it extends. It's also likely that we will learn new techniques and improvments to source generators over time, increasing the changes for bugfixes and features. +Contrast this to source generators which can take a very small amount of user source to produce a large amount of generated code. This large amount of generated code contains significant complexity which is loosely specified. Its behavior is specific to the component it extends. It's likely that we'd need to bugfix this code over time and change it as we develop features in the components it extends. It is also likely that we will learn new techniques and improvments to source generators over time, increasing the changes for bugfixes and features. Users expect stability for projects which do not change, for example a project targeting an LTS release. It is much more difficult to ensure this stability when we need to significantly components which contribute complexity to that project. @@ -136,17 +136,17 @@ A repository may have projects targeting multiple frameworks. These frameworks ### Should all inbox source generators be tied to a shared framework? -No, some source generators might be independent of libraries in a shared framework: these could go in a non-side-by-side location (eg: analyzers folder). Other source generators may not contain functionality, but may instead just build some convenience API on top of existing public API. These could be considered stable enough to place in a non-side-by-side location. +No, some source generators might be independent of libraries in a shared framework: these could go in a non-side-by-side location (eg: analyzers folder). Other source generators may not contain functionality but may instead just build some convenience API on top of existing public API. These could be considered stable enough to place in a non-side-by-side location. ### Should only source generators be shipped this way? What about analyzers and code-fixes? -Nothing is preventing analyzers and code fixes from shipping in the same manner. If it's desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. +Nothing is preventing analyzers and code fixes from shipping in the same manner. If it is desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. ### Why in the ref pack, why not some other "analyzer" pack? The SDK already has a means for acquisition and selection of ref packs. The inputs to the selection of ref packs are the same as that of source generators (Version, FrameworkReference, and TargetFramework), and the outputs are consumed by the same phase (compile) with the same level of cross-targeting granularity (TargetFramework and references). -It may be desirable to create a separate package just for analyzers in order to reduce risk in servicing. We support servicing ref packs today, but we have to be careful not to expose new API nor change any assembly identities. In my opinion it makes more sense to invest efforts in ensuring safe servicing of ref packs, with potential increased complexity in our own builds. There is benefit in having servicable ref packs regardless and this keeps additional complexity (resolution of additional pack) out of user builds. +It may be desirable to create a separate package just for analyzers to reduce risk in servicing. We support servicing ref packs today, but we must be careful not to expose new API nor change any assembly identities. In my opinion it makes more sense to invest efforts in ensuring safe servicing of ref packs, with potential increased complexity in our own builds. There is benefit in having servicable ref packs regardless and this keeps additional complexity (resolution of additional pack) out of user builds. ### Why in the ref pack, why not runtime pack? @@ -154,9 +154,9 @@ Runtime packs are currently independent of compile. Sourcing inputs for compile ### Why in the ref pack, why not a dedicated NuGet package? -It's a goal for source-generators to be part of the default experience. Additionally the default experience should not require a restore step. As such analyzers cannot be delivered by NuGet packages alone. +It is a goal for source-generators to be part of the default experience. Additionally, the default experience should not require a restore step. As such analyzers cannot be delivered by NuGet packages alone. -NuGet packages are a good option in addition to the ref pack for components which already ship as NuGet packages, eg System.Text.Json. In this way the source generator can deliver the integrated experience of being *part of* the library. +NuGet packages are a good option in addition to the ref pack for components which already ship as NuGet packages, e.g. System.Text.Json. In this way the source generator can deliver the integrated experience of being *part of* the library. Standalone source-generator NuGet packages may be used for components which are in the shared framework when the source-generator is not ready to stabalize at the same time as the shared framework, and the component inside the shared framework does not need to be changed. If the component in the shared framework needs to be changed, it should be done so by either a library+generator NuGet package (eg: System.Text.Json) or an experimental build of the entire framework, typically from a codebase like https://github.com/dotnet/runtimelab. From 94846573729b805691b98b05cb2d81332fa50c44 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Thu, 25 Feb 2021 18:27:54 -0800 Subject: [PATCH 06/16] Address feedback --- accepted/2021/InboxSourceGenerators.md | 44 ++++++++++++++++---------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 3ced1343b..647da1845 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -3,7 +3,7 @@ **PM** [Immo Landwerth](https://github.com/terrajobst) | **Dev** [Eric StJohn](https://github.com/ericstj) -Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a user's code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and static emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This technique was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to specific components within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. +Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a user's code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and statically emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This technique was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to specific components within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. ## Scenarios and User Experience @@ -13,11 +13,11 @@ A developer can use source generators for components in NuGet packages by refere When a component ships both in a NuGet package and a framework, the developer has a consistent experience with the source generator as they do with the library itself. If the package referenced is newer than the framework the source generator from the package will be used as is already the case for the library in that package. Similarly, if the framework is newer than the NuGet package the framework's source generator will be used as is already the case for the library in the framework. -A developer can receive updates to a source generator through normal channels which they already obtain updates today. +A developer can receive updates to a source generator through normal channels by which they already obtain updates today. -A developer can update their version of Visual Studio or Dotnet SDK and have confidence that new versions of the toolchain will not impact their applications which target older frameworks, outside of known and published servicing updates. +A developer can update their version of Visual Studio or .NET SDK and have confidence that new versions of the toolchain will not impact their applications which target older frameworks, outside of known and published servicing updates. -A developer has confidence that their library using inbox source generators will continue to work in the same way a library that does not use inbox source generators works on future versions of .NET. Source gener +A developer has confidence that their library using inbox source generators will continue to work in the same way a library that does not use inbox source generators works on future versions of .NET. ## Requirements @@ -50,9 +50,9 @@ A developer has confidence that their library using inbox source generators will ### Framework provided source generators -Framework provided source generators should have a mechanism to be exposed and versioned along side the framework and library which they extend. These source generators will be passed to the compiler when that framework is targeted. These source generators will not be used when the framework is not targeted. For example, ASP.NET specific source generators will not be used when ASP.NET is not referenced. The version of the source generators specific to the TargetFramework version will be used. For example, when targeting .NET 6.0 the source generators specific to net6.0 will be used and not those for .NET 7.0, even when using the SDK that ships with .NET 7.0. +Framework provided source generators should have a mechanism to be exposed and versioned along side the framework and library which they extend. These source generators will be passed to the compiler when that framework is targeted. These source generators will not be used when the framework is not targeted. For example, ASP.NET specific source generators will not be used when ASP.NET is not referenced. The version of the source generators specific to the TargetFramework version will be used. For example, when targeting .NET 6.0 the source generators specific to net6.0 will be used and not those for .NET 7.0, even when using the SDK that ships with .NET 7.0. -Source generators, analyzers, and code fixes are all represented and communicated in the same manner to the compiler, as analyzer dlls using the `-analyzer` switch. Though this document is motivated by describing the process for source generators it will work in the same manner for analyzers. In some places we may refer to the assemblies containing source generators as analyzers due to the way they are understood by the compiler, package manager, and SDK. +Source generators and analyzers are represented and communicated in the same manner to the compiler, as analyzer dlls using the `-analyzer` switch. Though this document is motivated by describing the process for source generators it will work in the same manner for analyzers. In some places we may refer to the assemblies containing source generators as analyzers due to the way they are understood by the compiler, package manager, and SDK. To facilitate this, we will package source generators inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [NuGet conventions for describing analyzers](https://docs.microsoft.com/en-us/nuget/guides/analyzers-conventions) with respect to the location in the package, however the SDK will not probe this path. All source generators and their metadata will be listed in FrameworkList.xml, similar to references. Analyzers will be added as `File` elements with `Type="Analyzer"` (constrasting to `Type="Managed"` which is currently used for references) and optionally `Language="cs|vb"`. Omitting the `Language` attribute means the analyzer applies to all languages. The .NET SDK will be responsible for locating the appropriate source generators from the refpack, based on the project's `$(Language)` and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other `@(Analyzer)` sources as described in the following section. When viewed from the IDE it should be clear that the source of the `Analyzer` is the framework reference, similarly to how it is clear that the source of NuGet package analyzers come from packaages. @@ -60,15 +60,15 @@ Alternative designs are listed in the [Q & A](#Q%20&%20A) section below. ### Source generator conflict resolution -Source generators and analyzers are passed to the compiler as @(Analyzer) items. These items may come from NuGet packages, be built into the SDK targets, be directly defined by the user via ProjectReference or raw Analyzer items, or be provided by the new framework mechanism described in this document. All should be considered for conflict resolution based on the same constraint. +Source generators and analyzers are passed to the compiler as `@(Analyzer)` items. These items may come from NuGet packages, be built into the SDK targets, be directly defined by the user via `ProjectReference` or raw `Analyzer` items, or be provided by the new framework mechanism described in this document. All should be considered for conflict resolution based on the same constraint. -The .NET SDK already does conflict resolution for other asset types: references and copy-local runtime assets. It should do the same for source generators, ore more generally Analyzers. Analyzers need not have "platform manifest" inputs for conflict resolution since all analyzers will be available during build, unlike runtime files which may not be available. Comparison for the sake of conflict resolution can consider the same assembly and file version rules as reference files. When finding two or more source generators with the same name conflict resolution will select the generator with higher assembly version, then higher file version, then that which is in the framework. This parallels the same rules followed by reference assemblies. The SDK may choose to optimize this comparison as it does for reference assemblies, by including a data file that includes analyzer metadata. +The .NET SDK already does conflict resolution for other asset types: references and copy-local runtime assets. It should do the same for source generators, or more generally Analyzers. Analyzers need not have "platform manifest" inputs for conflict resolution since all analyzers will be available during build, unlike runtime files which may not be available. Comparison for the sake of conflict resolution can consider the same assembly and file version rules as reference files. When finding two or more source generators with the same name conflict resolution will select the generator with higher assembly version, then higher file version, then that which is in the framework. This parallels the same rules followed by reference assemblies. The SDK may choose to optimize this comparison as it does for reference assemblies, by including a data file that includes analyzer metadata. Source generator conflict resolution should run before compile, but after all NuGet package assets have been evaluated. Specific sequencing, public/private target naming, extensibility will not be specified here but should follow best practice as determined by the SDK team. ### Source generator versioning -Source generators should be strongly named and update assembly version with every public stable release (GA and servicing). This ensures that the publicly shipped source generators can be loaded side-by-side in the same process and follows our versioning guidelines for components which ship and must run on .NETFramework. Source generators need not update assembly version during pre-release but should update file version to facilitate well behaved distribution by installers. In general source generators need to follow all the same guidelines as other code that ships out of dotnet/runtime and runs outside the shared framework. +Source generators should be strongly named and update assembly version with every public stable release (GA and servicing). This ensures that the publicly shipped source generators can be loaded side-by-side in the same process and follows our versioning guidelines for components which ship and must run on .NET Framework. Source generators need not update assembly version during pre-release but should update file version to facilitate well behaved distribution by installers. In general source generators need to follow all the same guidelines as other code that ships out of dotnet/runtime and runs outside the shared framework. ### Source generator compatibility concerns @@ -84,7 +84,9 @@ Generated code should not be generated in System namespaces, it should prefer th Source generators contribute significant code to user assemblies and that code may have issues. We have very little control over changing customer assemblies once they have consumed a source generator. We cannot and should not attempt to change the behavior of customer's assemblies (eg: hot patch) to fix issues. Should an issue be found we should fix it in the source generator and ship a new version, just as we would if we found an issue with compiler. Customers desiring this fix must proactively acquire the new code-generator, typically through an updated SDK which carries the ref pack, rebuild their code, and update their production applications. -Due to the limited servicing agility for code-generated code, we should be very careful about the complexity of the code which is generate. Where possible we should limit code-generated code to "glue code", only that which must be specific to the user assembly or types. We should try to put more complex code inside the framework or library itself, even if it means exposing public API that is specifically for source generated code to call. +Due to the limited servicing agility for code-generated code, we should be very careful about the complexity of the code which is generated. Where possible we should limit generated code to "glue code", only that which must be specific to the user's assembly or types. We should try to put more complex code inside the framework or library itself, even if it means exposing public API that is specifically for source generated code to call. + +Inbox source generators will be serviced in their side-by-side location. Should a bug exist in a osurce generator we will evaluate the bug against the bar for each framework it exists and patch them independently. In this way the servicing process for these source generators will match the .NET runtime. **Open issue:** should we do anything to make it easier to identify assemblies which might need an update? We can consider some case studies. @@ -110,17 +112,21 @@ We will need to better understand what customer's expectations are around servic Though the following are not specific to inbox source generators, these concerns are worth reviewing to describe how they apply to framework specific source generators. -Source generator assemblies execute in the compiler process and must target .NETStandard in order run on both .NETFramework and .NETCore. Source generators should support generating source for all TargetFrameworks supported by their companion library. In the case of inbox source generators discussed in this document this need only be the TargetFramework the generator ships in. In the case of a NuGet package provided source generator, this should be all frameworks supported by package. +Source generator assemblies execute in the compiler process and must target .NET Standard in order run on both .NET Framework and .NET Core. Source generators should support generating source for all target frameworks supported by their companion library. In the case of inbox source generators discussed in this document this need only be the target framework the generator ships in. In the case of a NuGet package provided source generator, this should be all frameworks supported by package. Source generators should limit their dependencies. There is no scheme for encoding framework-specific nor runtime-specific implementations of dependencies or source generators based on the runtime environment so source generators should avoid any dependencies on assemblies which need to differ by framework or runtime. Source generators may depend on packages which are known dependencies of the compiler and should depend on versions less than or equal to that provided by the compiler and should not include that dependency in their package. -Source generators should not rely on referencing nor executing framework types they wish to extend. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. For example, API added to help a source generator do its job may not be present when running on .NETFramework or the .NETCore version the compiler is running on. There is no type-equivalence guaranteed between types loaded in the compiler process and those of the user code that is under analysis. For example, a source generator cannot instantiate attribute instances in the compiler process from user references to those attributes in source. Should a source generator need to identify type usage it will need to examine user code references by name or resolve types in the user's references to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library helper code when generating source. For example, serializer code which needs to examine the shape of user types can be shared between the runtime library and source generator. Common abstractions can be made over metadata to allow the same code to handle different sources of metadata (source vs reflection). +Source generators should not rely on referencing nor executing framework types they wish to extend. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. For example, API added to help a source generator do its job may not be present when running on .NET Framework or the .NET Core version the compiler is running on. There is no type-equivalence guaranteed between types loaded in the compiler process and those of the user code that is under analysis. For example, a source generator cannot instantiate attribute instances in the compiler process from user references to those attributes in source. Should a source generator need to identify type usage it will need to examine user code references by name or resolve types in the user's references to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library helper code when generating source. For example, serializer code which needs to examine the shape of user types can be shared between the runtime library and source generator. Common abstractions can be made over metadata to allow the same code to handle different sources of metadata (source vs reflection). ## Q & A ### Why not just deliver source generators in the SDK same as roslyn analyzers? -Doing so would require source generators to carefully version and test across all framework versions. This would increase testing cost for source generators and increase risk when shipping updates to source generators since they would apply to past, stable TargetFrameworks. Conceptually source generators are lifting a substantial amount of runtime functionality out of a library and executing that at design time. This code is directly included in the user's application and contributes to runtime behavior. +Doing so would require source generators to carefully version and test across all framework versions. This would increase testing cost for source generators and increase risk when shipping updates to source generators since they would apply to past, stable target frameworks. Conceptually source generators are lifting a substantial amount of runtime functionality out of a library and executing that at design time. This code is directly included in the user's application and contributes to runtime behavior. + +### Can't the analyzer handle delivering different behavior per library version? + +We could implement the framework/library specific behavior in the analyzer itself, like the compiler does with `$(LangVersion)`. We could use an input like the project's `$(TargetFramework)`, metadata in the reference asssembly like a source generator version experessed in an attribute `AssemblyMetadataAttribute("UseMySourceGeneratorVersion", "1.2")`, or even do API probing to determine which features are supported by the library. Whichever input chosen would need to directly correlate with the beavior of the library selection (conflict resolution), ruling out `TargetFramework`, and be stable across servicing updates to provide determinsm to existing projects. These solutions introduce complexity into the source generator and risk for the end user. They would require us to build and test analyzers across all older targets: we would need to test the latest source generator across all previous versions of the library. They also introduce risk since they could impact a customer's application when using the latest SDK but targeting an older, LTS framework, since those would use the newest analyzer undergoing churn that is not scrutinized through our normal servicing process. ### Inplace servicing updates are good enough for the compiler, why not source generators? @@ -130,6 +136,12 @@ Contrast this to source generators which can take a very small amount of user so Users expect stability for projects which do not change, for example a project targeting an LTS release. It is much more difficult to ensure this stability when we need to significantly components which contribute complexity to that project. +### Isn't this a lot of servicing if we find a bug? + +Although this seems like more changes than if behavior was in the compiler-distributed analyzers it is not. If we cared enough about a bug to service an analyzer that runs on a stable .NET version we would also want to service the SDK for that stable .NET Version: including the same number of changes and branches. In fact, this strategy *reduces* servicing in the case where a bug only exists in a single version of source generator. + +What this strategy does do is reduce risk. We eliminate the channel where the latest SDK would contain changes that impacted users' projects targeting older .NET versions. That is a good thing as it reduces the need to test those configurations and ensures we can garuntee the quality and stability of our stable .NET releases. + ### Is it bad to have multiple versions of a source generator possibly loaded by a repository? A repository may have projects targeting multiple frameworks. These frameworks may contain different versions of the same source generator. When building the repository this may require the same compiler server instance to load multiple copies of a source generator. The same compiler would not likely load multiple copies of analyzers which do not ship side by side. @@ -140,7 +152,7 @@ No, some source generators might be independent of libraries in a shared framewo ### Should only source generators be shipped this way? What about analyzers and code-fixes? -Nothing is preventing analyzers and code fixes from shipping in the same manner. If it is desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. +Nothing is preventing analyzers and code fixes from shipping in the same manner. If it is desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. We should only do this if we have a good reason, stable analyzers that are lanaguage extensions or tied to stable public API should ship non-side-by-side with the compiler as they do today. Analyzers that need to validate source generator usage, or analyzers that are closely tied to runtime functionality may consider shipping side-by-side with the framework. ### Why in the ref pack, why not some other "analyzer" pack? @@ -156,9 +168,9 @@ Runtime packs are currently independent of compile. Sourcing inputs for compile It is a goal for source-generators to be part of the default experience. Additionally, the default experience should not require a restore step. As such analyzers cannot be delivered by NuGet packages alone. -NuGet packages are a good option in addition to the ref pack for components which already ship as NuGet packages, e.g. System.Text.Json. In this way the source generator can deliver the integrated experience of being *part of* the library. +NuGet packages are a good option in addition to the ref pack for components which already ship as NuGet packages, such as `System.Text.Json`. In this way the source generator can deliver the integrated experience of being *part of* the library. -Standalone source-generator NuGet packages may be used for components which are in the shared framework when the source-generator is not ready to stabalize at the same time as the shared framework, and the component inside the shared framework does not need to be changed. If the component in the shared framework needs to be changed, it should be done so by either a library+generator NuGet package (eg: System.Text.Json) or an experimental build of the entire framework, typically from a codebase like https://github.com/dotnet/runtimelab. +Standalone source-generator NuGet packages may be used for components which are in the shared framework when the source-generator is not ready to stabalize at the same time as the shared framework, and the component inside the shared framework does not need to be changed. If the component in the shared framework needs to be changed, it should be done so by either a library+generator NuGet package (`System.Text.Json`) or an experimental build of the entire framework, typically from a codebase like [dotnet/runtimelab](https://github.com/dotnet/runtimelab). ### I really really need to execute runtime code on the TargetFramework! From f3b2bf6cd1499047463fba92e0c5d43183124c56 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Fri, 26 Feb 2021 09:57:26 -0800 Subject: [PATCH 07/16] Fix typo Co-authored-by: Jan Kotas --- accepted/2021/InboxSourceGenerators.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 647da1845..1be19ee64 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -170,7 +170,7 @@ It is a goal for source-generators to be part of the default experience. Additi NuGet packages are a good option in addition to the ref pack for components which already ship as NuGet packages, such as `System.Text.Json`. In this way the source generator can deliver the integrated experience of being *part of* the library. -Standalone source-generator NuGet packages may be used for components which are in the shared framework when the source-generator is not ready to stabalize at the same time as the shared framework, and the component inside the shared framework does not need to be changed. If the component in the shared framework needs to be changed, it should be done so by either a library+generator NuGet package (`System.Text.Json`) or an experimental build of the entire framework, typically from a codebase like [dotnet/runtimelab](https://github.com/dotnet/runtimelab). +Standalone source-generator NuGet packages may be used for components which are in the shared framework when the source-generator is not ready to stabilize at the same time as the shared framework, and the component inside the shared framework does not need to be changed. If the component in the shared framework needs to be changed, it should be done so by either a library+generator NuGet package (`System.Text.Json`) or an experimental build of the entire framework, typically from a codebase like [dotnet/runtimelab](https://github.com/dotnet/runtimelab). ### I really really need to execute runtime code on the TargetFramework! From 22d1415c9ec40ee64f70c8ebc41a96e620f98529 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 2 Mar 2021 10:47:23 -0800 Subject: [PATCH 08/16] Include notes about mode of failure and source build --- accepted/2021/InboxSourceGenerators.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 647da1845..c665dc6af 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -56,6 +56,8 @@ Source generators and analyzers are represented and communicated in the same man To facilitate this, we will package source generators inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [NuGet conventions for describing analyzers](https://docs.microsoft.com/en-us/nuget/guides/analyzers-conventions) with respect to the location in the package, however the SDK will not probe this path. All source generators and their metadata will be listed in FrameworkList.xml, similar to references. Analyzers will be added as `File` elements with `Type="Analyzer"` (constrasting to `Type="Managed"` which is currently used for references) and optionally `Language="cs|vb"`. Omitting the `Language` attribute means the analyzer applies to all languages. The .NET SDK will be responsible for locating the appropriate source generators from the refpack, based on the project's `$(Language)` and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other `@(Analyzer)` sources as described in the following section. When viewed from the IDE it should be clear that the source of the `Analyzer` is the framework reference, similarly to how it is clear that the source of NuGet package analyzers come from packaages. +Existing public functionality which controls the behavior of shared-framework references should also apply to source generators. `$(DisableImplicitFrameworkReferences)` should disable the creation of `@(Analyzer)` items. Any other property which might disable the creation of `@(Reference)` items from the ref-pack should also disable the creation of `@(Analyzer)` items. Additionally, a new property should be introduced to disable the creation of `@(Analyzer)` items: `$(DisableFrameworkReferenceAnalyzers)`. + Alternative designs are listed in the [Q & A](#Q%20&%20A) section below. ### Source generator conflict resolution @@ -80,6 +82,10 @@ Source generators should avoid emitting public API in a user's projects. Such A Generated code should not be generated in System namespaces, it should prefer the user's namespace. Generated code should be triple-slashed commented and be free from compiler warnings. Generated code should avoid name collisions with user code and provide affordances for resolving name conflicts. As much as possible generated code should meet the quality standards of .NET library code, using nullable annotations, meeting framework design guidelines, naming guidelines, etc. It should feel like a natural extension of the library. +### Deterministic failure + +The absence of a required source generator from code that requires it should produce a clear diagnosable error message from the compiler, like the error from a missing reference. A codebase that was written to make use of a source-generator should never compile and silently omit the source generated code. For example: if a source generator uses and attribute to activate the generator and then generates extension methods with overloads that are automatically preferred, the source generator should define the attribute in the source generator, not the framework library. This will ensure that the project fails to compile if the source generator is absent. + ### Source generator servicing Source generators contribute significant code to user assemblies and that code may have issues. We have very little control over changing customer assemblies once they have consumed a source generator. We cannot and should not attempt to change the behavior of customer's assemblies (eg: hot patch) to fix issues. Should an issue be found we should fix it in the source generator and ship a new version, just as we would if we found an issue with compiler. Customers desiring this fix must proactively acquire the new code-generator, typically through an updated SDK which carries the ref pack, rebuild their code, and update their production applications. @@ -175,3 +181,7 @@ Standalone source-generator NuGet packages may be used for components which are ### I really really need to execute runtime code on the TargetFramework! You probably need a designer or independent build component that has a host process that can leverage the runtime targeting information of the application. At the moment this doesn't have a great pattern to follow with source generators. The compiler does not provide a host, nor does it know anything about target runtime (or in some cases executable frameworks). + +### How does this impact source build? + +For Linux distribution .NET defines a process from which the .NET product can be built from source: [dotnet/source-build](https://github.com/dotnet/source-build). This process requires building the entire .NET product without any pre-built binaries. Some components which make up .NET target older frameworks which aren't built from the latest codebase. This is done because these components may need to run at design-time on a different framework (.NETFramework), or may also ship out-of-band as a NuGet package for use by libraries that work on many frameworks. To support building these components on past frameworks, source-build uses an alternate representation of reference assets for those frameworks: [dotnet/source-build-reference-packages](https://github.com/dotnet/source-build-reference-packages). In order to satisfy build-from-source packages should only contain reference-API and metadata, no executable code. As a result, the source-build-reference-packages that represent past framework versions will omit source generators. This could be a problem for any component which needs to build during source-build and targets this old framework, for example a project targeting .NET 6.0 in the .NET 7.0 source build. That component would fail to build during source build due to the missing source-generator (see [Deterministic failure](#Deterministic%20failure)). That component can mitigate the failure by either including a .NET 7.0 configuration for source build. Alternatively, if the dependent source-generator is also out-of-band (for example: System.Text.Json) the failing component may reference the latest dependent out-of-band package to get the latest source-generator from the package instead of the ref-pack. To force components to make this mitigation early, we will make sure that Arcade disables source-generators from ref-packs which are not the latest when building for source build. \ No newline at end of file From ebf9ee63629dc5ddf3ee6c175020a281a58ef4e5 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Wed, 3 Mar 2021 22:32:30 -0800 Subject: [PATCH 09/16] Fix typo --- accepted/2021/InboxSourceGenerators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 279ed91e5..1e9f9148c 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -92,7 +92,7 @@ Source generators contribute significant code to user assemblies and that code m Due to the limited servicing agility for code-generated code, we should be very careful about the complexity of the code which is generated. Where possible we should limit generated code to "glue code", only that which must be specific to the user's assembly or types. We should try to put more complex code inside the framework or library itself, even if it means exposing public API that is specifically for source generated code to call. -Inbox source generators will be serviced in their side-by-side location. Should a bug exist in a osurce generator we will evaluate the bug against the bar for each framework it exists and patch them independently. In this way the servicing process for these source generators will match the .NET runtime. +Inbox source generators will be serviced in their side-by-side location. Should a bug exist in a source generator we will evaluate the bug against the bar for each framework it exists and patch them independently. In this way the servicing process for these source generators will match the .NET runtime. **Open issue:** should we do anything to make it easier to identify assemblies which might need an update? We can consider some case studies. @@ -184,4 +184,4 @@ You probably need a designer or independent build component that has a host proc ### How does this impact source build? -For Linux distribution .NET defines a process from which the .NET product can be built from source: [dotnet/source-build](https://github.com/dotnet/source-build). This process requires building the entire .NET product without any pre-built binaries. Some components which make up .NET target older frameworks which aren't built from the latest codebase. This is done because these components may need to run at design-time on a different framework (.NETFramework), or may also ship out-of-band as a NuGet package for use by libraries that work on many frameworks. To support building these components on past frameworks, source-build uses an alternate representation of reference assets for those frameworks: [dotnet/source-build-reference-packages](https://github.com/dotnet/source-build-reference-packages). In order to satisfy build-from-source packages should only contain reference-API and metadata, no executable code. As a result, the source-build-reference-packages that represent past framework versions will omit source generators. This could be a problem for any component which needs to build during source-build and targets this old framework, for example a project targeting .NET 6.0 in the .NET 7.0 source build. That component would fail to build during source build due to the missing source-generator (see [Deterministic failure](#Deterministic%20failure)). That component can mitigate the failure by either including a .NET 7.0 configuration for source build. Alternatively, if the dependent source-generator is also out-of-band (for example: System.Text.Json) the failing component may reference the latest dependent out-of-band package to get the latest source-generator from the package instead of the ref-pack. To force components to make this mitigation early, we will make sure that Arcade disables source-generators from ref-packs which are not the latest when building for source build. \ No newline at end of file +For Linux distribution .NET defines a process from which the .NET product can be built from source: [dotnet/source-build](https://github.com/dotnet/source-build). This process requires building the entire .NET product without any pre-built binaries. Some components which make up .NET target older frameworks which aren't built from the latest codebase. This is done because these components may need to run at design-time on a different framework (.NETFramework), or may also ship out-of-band as a NuGet package for use by libraries that work on many frameworks. To support building these components on past frameworks, source-build uses an alternate representation of reference assets for those frameworks: [dotnet/source-build-reference-packages](https://github.com/dotnet/source-build-reference-packages). In order to satisfy build-from-source packages should only contain reference-API and metadata, no executable code. As a result, the source-build-reference-packages that represent past framework versions will omit source generators. This could be a problem for any component which needs to build during source-build and targets this old framework, for example a project targeting .NET 6.0 in the .NET 7.0 source build. That component would fail to build during source build due to the missing source-generator (see [Deterministic failure](#Deterministic%20failure)). That component can mitigate the failure by either including a .NET 7.0 configuration for source build. Alternatively, if the dependent source-generator is also out-of-band (for example: System.Text.Json) the failing component may reference the latest dependent out-of-band package to get the latest source-generator from the package instead of the ref-pack. To force components to make this mitigation early, we will make sure that Arcade disables source-generators from ref-packs which are not the latest when building for source build. From ad3e7b8d0ee6ac37808e6b357a2835d94354dfed Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Fri, 5 Mar 2021 12:15:45 -0800 Subject: [PATCH 10/16] Apply suggestions from code review Co-authored-by: Elinor Fung --- accepted/2021/InboxSourceGenerators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 1e9f9148c..3d94cbc7b 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -84,7 +84,7 @@ Generated code should not be generated in System namespaces, it should prefer th ### Deterministic failure -The absence of a required source generator from code that requires it should produce a clear diagnosable error message from the compiler, like the error from a missing reference. A codebase that was written to make use of a source-generator should never compile and silently omit the source generated code. For example: if a source generator uses and attribute to activate the generator and then generates extension methods with overloads that are automatically preferred, the source generator should define the attribute in the source generator, not the framework library. This will ensure that the project fails to compile if the source generator is absent. +The absence of a required source generator from code that requires it should produce a clear diagnosable error message from the compiler, like the error from a missing reference. A codebase that was written to make use of a source-generator should never compile and silently omit the source generated code. For example: if a source generator uses an attribute to activate the generator and then generates extension methods with overloads that are automatically preferred, the source generator should define the attribute in the source generator, not the framework library. This will ensure that the project fails to compile if the source generator is absent. ### Source generator servicing @@ -92,7 +92,7 @@ Source generators contribute significant code to user assemblies and that code m Due to the limited servicing agility for code-generated code, we should be very careful about the complexity of the code which is generated. Where possible we should limit generated code to "glue code", only that which must be specific to the user's assembly or types. We should try to put more complex code inside the framework or library itself, even if it means exposing public API that is specifically for source generated code to call. -Inbox source generators will be serviced in their side-by-side location. Should a bug exist in a source generator we will evaluate the bug against the bar for each framework it exists and patch them independently. In this way the servicing process for these source generators will match the .NET runtime. +Inbox source generators will be serviced in their side-by-side location. Should a bug exist in a source generator, we will evaluate the bug against the bar for each framework in which it exists and patch them independently. In this way the servicing process for these source generators will match the .NET runtime. **Open issue:** should we do anything to make it easier to identify assemblies which might need an update? We can consider some case studies. From 7f315007c2e66c46f14dd6dfaf8068ea0d6088d6 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Fri, 5 Mar 2021 14:23:09 -0800 Subject: [PATCH 11/16] Address feedback --- accepted/2021/InboxSourceGenerators.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 3d94cbc7b..b188339c3 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -35,6 +35,7 @@ A developer has confidence that their library using inbox source generators will - Define any changes to the source generator architecture. - Define any implementation details of source generators or source generator tests. - Define a process around internal or non-shipping source generators +- Define guidelines applicable to all source generators. ## Stakeholders and Reviewers @@ -56,7 +57,7 @@ Source generators and analyzers are represented and communicated in the same man To facilitate this, we will package source generators inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [NuGet conventions for describing analyzers](https://docs.microsoft.com/en-us/nuget/guides/analyzers-conventions) with respect to the location in the package, however the SDK will not probe this path. All source generators and their metadata will be listed in FrameworkList.xml, similar to references. Analyzers will be added as `File` elements with `Type="Analyzer"` (constrasting to `Type="Managed"` which is currently used for references) and optionally `Language="cs|vb"`. Omitting the `Language` attribute means the analyzer applies to all languages. The .NET SDK will be responsible for locating the appropriate source generators from the refpack, based on the project's `$(Language)` and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other `@(Analyzer)` sources as described in the following section. When viewed from the IDE it should be clear that the source of the `Analyzer` is the framework reference, similarly to how it is clear that the source of NuGet package analyzers come from packaages. -Existing public functionality which controls the behavior of shared-framework references should also apply to source generators. `$(DisableImplicitFrameworkReferences)` should disable the creation of `@(Analyzer)` items. Any other property which might disable the creation of `@(Reference)` items from the ref-pack should also disable the creation of `@(Analyzer)` items. Additionally, a new property should be introduced to disable the creation of `@(Analyzer)` items: `$(DisableFrameworkReferenceAnalyzers)`. +Existing public functionality which controls the behavior of shared-framework references should also apply to source generators. `$(DisableImplicitFrameworkReferences)` should disable the creation of `@(Analyzer)` items. Any other property which might disable the creation of `@(Reference)` items from the ref-pack should also disable the creation of `@(Analyzer)` items. Additionally, a new property should be introduced to disable the creation of `@(Analyzer)` items: `$(DisableFrameworkReferenceAnalyzers)`. No mechanism will be provided to select individual `@(Analyzer)` items, just as there is no mechanism to select individual `@(Reference)` items. Alternative designs are listed in the [Q & A](#Q%20&%20A) section below. @@ -92,7 +93,7 @@ Source generators contribute significant code to user assemblies and that code m Due to the limited servicing agility for code-generated code, we should be very careful about the complexity of the code which is generated. Where possible we should limit generated code to "glue code", only that which must be specific to the user's assembly or types. We should try to put more complex code inside the framework or library itself, even if it means exposing public API that is specifically for source generated code to call. -Inbox source generators will be serviced in their side-by-side location. Should a bug exist in a source generator, we will evaluate the bug against the bar for each framework in which it exists and patch them independently. In this way the servicing process for these source generators will match the .NET runtime. +Inbox source generators will be serviced in their framework-specific location by shipping a new version of the ref-pack. Should a bug exist in a source generator, we will evaluate the bug against the bar for each framework in which it exists and patch them independently. In this way the servicing process for these source generators will match the .NET runtime. **Open issue:** should we do anything to make it easier to identify assemblies which might need an update? We can consider some case studies. @@ -103,7 +104,8 @@ If a runtime assembly has a bug, we have multiple avenues of detection. We coul For means of detection we have: 1. Manual 2. PDB indicator: source generator assembly version, file version, hash -3. Assembly metadata in consuming user project: GeneratedCodeAttribute("System.Foo.Generator", "5.0.0+cf258a14b70ad9069470a108f13765e0e5988f51") +3. Assembly attribte in consuming user `[assembly: AssemblyMetadata("CodeGenerator", "System.Foo.Generator, 5.0.0+cf258a14b70ad9069470a108f13765e0e5988f51")` +3. Attribute on generated types/members: `[GeneratedCode("System.Foo.Generator", "5.0.0+cf258a14b70ad9069470a108f13765e0e5988f51")` 4. Runtime breadcrumbs For means of update we have: @@ -124,6 +126,8 @@ Source generators should limit their dependencies. There is no scheme for encod Source generators should not rely on referencing nor executing framework types they wish to extend. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. For example, API added to help a source generator do its job may not be present when running on .NET Framework or the .NET Core version the compiler is running on. There is no type-equivalence guaranteed between types loaded in the compiler process and those of the user code that is under analysis. For example, a source generator cannot instantiate attribute instances in the compiler process from user references to those attributes in source. Should a source generator need to identify type usage it will need to examine user code references by name or resolve types in the user's references to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library helper code when generating source. For example, serializer code which needs to examine the shape of user types can be shared between the runtime library and source generator. Common abstractions can be made over metadata to allow the same code to handle different sources of metadata (source vs reflection). +Source generators should localize any diagnostic messages displayed to the user. This can be done using localized resource sattelite assemblies. These assemblies should be distributed with the source generator assembly. + ## Q & A ### Why not just deliver source generators in the SDK same as roslyn analyzers? @@ -150,15 +154,15 @@ What this strategy does do is reduce risk. We eliminate the channel where the l ### Is it bad to have multiple versions of a source generator possibly loaded by a repository? -A repository may have projects targeting multiple frameworks. These frameworks may contain different versions of the same source generator. When building the repository this may require the same compiler server instance to load multiple copies of a source generator. The same compiler would not likely load multiple copies of analyzers which do not ship side by side. +A repository may have projects targeting multiple frameworks. These frameworks may contain different versions of the same source generator. When building the repository this may require the same compiler instance to load multiple copies of a source generator. The same compiler would not load multiple copies of source generators which ship in a non-framework specific location. This should work and be supported based on source generator versioning. ### Should all inbox source generators be tied to a shared framework? -No, some source generators might be independent of libraries in a shared framework: these could go in a non-side-by-side location (eg: analyzers folder). Other source generators may not contain functionality but may instead just build some convenience API on top of existing public API. These could be considered stable enough to place in a non-side-by-side location. +No, some source generators might be independent of a target framework. One example of such a source generators may not contain functionality but may instead just build some convenience API on top of existing public API that is unlikely to ever change. These could be considered stable enough to place in a non-framework-specific location like the SDK's analyzer folder. Source generators which choose to ship this way should be highly specified and will need to support backwards compatibility and test on all frameworks. ### Should only source generators be shipped this way? What about analyzers and code-fixes? -Nothing is preventing analyzers and code fixes from shipping in the same manner. If it is desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. We should only do this if we have a good reason, stable analyzers that are lanaguage extensions or tied to stable public API should ship non-side-by-side with the compiler as they do today. Analyzers that need to validate source generator usage, or analyzers that are closely tied to runtime functionality may consider shipping side-by-side with the framework. +Nothing is preventing analyzers and code fixes from shipping in the same manner. If it is desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. We should only do this if we have a good reason, stable analyzers that are lanaguage extensions or tied to stable public API should ship non-framework-specific with the SDK as they do today. Analyzers that need to validate source generator usage, or analyzers that are closely tied to runtime functionality may consider shipping with the framework. ### Why in the ref pack, why not some other "analyzer" pack? From 084e31c0e128630137c5f7d846a386551139004a Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Fri, 26 Mar 2021 11:12:38 -0700 Subject: [PATCH 12/16] Apply suggestions from code review Co-authored-by: Jeff Handley --- accepted/2021/InboxSourceGenerators.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index b188339c3..c2ffc5864 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -104,7 +104,7 @@ If a runtime assembly has a bug, we have multiple avenues of detection. We coul For means of detection we have: 1. Manual 2. PDB indicator: source generator assembly version, file version, hash -3. Assembly attribte in consuming user `[assembly: AssemblyMetadata("CodeGenerator", "System.Foo.Generator, 5.0.0+cf258a14b70ad9069470a108f13765e0e5988f51")` +3. Assembly attribute in consuming user `[assembly: AssemblyMetadata("CodeGenerator", "System.Foo.Generator, 5.0.0+cf258a14b70ad9069470a108f13765e0e5988f51")` 3. Attribute on generated types/members: `[GeneratedCode("System.Foo.Generator", "5.0.0+cf258a14b70ad9069470a108f13765e0e5988f51")` 4. Runtime breadcrumbs @@ -114,7 +114,7 @@ For means of update we have: 3. Killbit in the runtime to prevent execution of user assemblies that contain vulnerable source generated code. 4. ~~Automatically update user assembly though a hotpatch applied by runtime or JIT~~ -We will need to better understand what customer's expectations are around servicing to decide if we should do any work on these axes. My initial inclination is to only have a PDB indicator and require users to subscribe to updates. +We will need to better understand what customers' expectations are around servicing to decide if we should do any work on these axes. My initial inclination is to only have a PDB indicator and require users to subscribe to updates. ### Functionality concerns for source generators @@ -126,7 +126,7 @@ Source generators should limit their dependencies. There is no scheme for encod Source generators should not rely on referencing nor executing framework types they wish to extend. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. For example, API added to help a source generator do its job may not be present when running on .NET Framework or the .NET Core version the compiler is running on. There is no type-equivalence guaranteed between types loaded in the compiler process and those of the user code that is under analysis. For example, a source generator cannot instantiate attribute instances in the compiler process from user references to those attributes in source. Should a source generator need to identify type usage it will need to examine user code references by name or resolve types in the user's references to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library helper code when generating source. For example, serializer code which needs to examine the shape of user types can be shared between the runtime library and source generator. Common abstractions can be made over metadata to allow the same code to handle different sources of metadata (source vs reflection). -Source generators should localize any diagnostic messages displayed to the user. This can be done using localized resource sattelite assemblies. These assemblies should be distributed with the source generator assembly. +Source generators should localize any diagnostic messages displayed to the user. This can be done using localized resource satellite assemblies. These assemblies should be distributed with the source generator assembly. ## Q & A @@ -150,7 +150,7 @@ Users expect stability for projects which do not change, for example a project t Although this seems like more changes than if behavior was in the compiler-distributed analyzers it is not. If we cared enough about a bug to service an analyzer that runs on a stable .NET version we would also want to service the SDK for that stable .NET Version: including the same number of changes and branches. In fact, this strategy *reduces* servicing in the case where a bug only exists in a single version of source generator. -What this strategy does do is reduce risk. We eliminate the channel where the latest SDK would contain changes that impacted users' projects targeting older .NET versions. That is a good thing as it reduces the need to test those configurations and ensures we can garuntee the quality and stability of our stable .NET releases. +What this strategy does do is reduce risk. We eliminate the channel where the latest SDK would contain changes that impacted users' projects targeting older .NET versions. That is a good thing as it reduces the need to test those configurations and ensures we can guarantee the quality and stability of our stable .NET releases. ### Is it bad to have multiple versions of a source generator possibly loaded by a repository? @@ -158,17 +158,17 @@ A repository may have projects targeting multiple frameworks. These frameworks ### Should all inbox source generators be tied to a shared framework? -No, some source generators might be independent of a target framework. One example of such a source generators may not contain functionality but may instead just build some convenience API on top of existing public API that is unlikely to ever change. These could be considered stable enough to place in a non-framework-specific location like the SDK's analyzer folder. Source generators which choose to ship this way should be highly specified and will need to support backwards compatibility and test on all frameworks. +No, some source generators might be independent of a target framework. One example of such: a source generator may not contain functionality, but may instead build some convenience API on top of existing public API that is unlikely to ever change. These could be considered stable enough to place in a non-framework-specific location like the SDK's analyzer folder. Source generators which choose to ship this way should be highly specified and will need to support backwards compatibility and test on all frameworks. ### Should only source generators be shipped this way? What about analyzers and code-fixes? -Nothing is preventing analyzers and code fixes from shipping in the same manner. If it is desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. We should only do this if we have a good reason, stable analyzers that are lanaguage extensions or tied to stable public API should ship non-framework-specific with the SDK as they do today. Analyzers that need to validate source generator usage, or analyzers that are closely tied to runtime functionality may consider shipping with the framework. +Nothing is preventing analyzers and code fixes from shipping in the same manner. If it is desirable for a such a component to ship with the framework rather than the compiler then that component may ship in this manner. We should only do this if we have a good reason; stable analyzers that are language extensions or tied to stable public API should ship non-framework-specific with the SDK as they do today. Analyzers that need to validate source generator usage, or analyzers that are closely tied to runtime functionality may consider shipping with the framework. ### Why in the ref pack, why not some other "analyzer" pack? The SDK already has a means for acquisition and selection of ref packs. The inputs to the selection of ref packs are the same as that of source generators (Version, FrameworkReference, and TargetFramework), and the outputs are consumed by the same phase (compile) with the same level of cross-targeting granularity (TargetFramework and references). -It may be desirable to create a separate package just for analyzers to reduce risk in servicing. We support servicing ref packs today, but we must be careful not to expose new API nor change any assembly identities. In my opinion it makes more sense to invest efforts in ensuring safe servicing of ref packs, with potential increased complexity in our own builds. There is benefit in having servicable ref packs regardless and this keeps additional complexity (resolution of additional pack) out of user builds. +It may be desirable to create a separate package just for analyzers to reduce risk in servicing. We support servicing ref packs today, but we must be careful not to expose new API nor change any assembly identities. It makes more sense to invest efforts in ensuring safe servicing of ref packs, with potential increased complexity in our own builds. There is benefit in having serviceable ref packs regardless and this keeps additional complexity (resolution of additional pack) out of user builds. ### Why in the ref pack, why not runtime pack? @@ -188,4 +188,4 @@ You probably need a designer or independent build component that has a host proc ### How does this impact source build? -For Linux distribution .NET defines a process from which the .NET product can be built from source: [dotnet/source-build](https://github.com/dotnet/source-build). This process requires building the entire .NET product without any pre-built binaries. Some components which make up .NET target older frameworks which aren't built from the latest codebase. This is done because these components may need to run at design-time on a different framework (.NETFramework), or may also ship out-of-band as a NuGet package for use by libraries that work on many frameworks. To support building these components on past frameworks, source-build uses an alternate representation of reference assets for those frameworks: [dotnet/source-build-reference-packages](https://github.com/dotnet/source-build-reference-packages). In order to satisfy build-from-source packages should only contain reference-API and metadata, no executable code. As a result, the source-build-reference-packages that represent past framework versions will omit source generators. This could be a problem for any component which needs to build during source-build and targets this old framework, for example a project targeting .NET 6.0 in the .NET 7.0 source build. That component would fail to build during source build due to the missing source-generator (see [Deterministic failure](#Deterministic%20failure)). That component can mitigate the failure by either including a .NET 7.0 configuration for source build. Alternatively, if the dependent source-generator is also out-of-band (for example: System.Text.Json) the failing component may reference the latest dependent out-of-band package to get the latest source-generator from the package instead of the ref-pack. To force components to make this mitigation early, we will make sure that Arcade disables source-generators from ref-packs which are not the latest when building for source build. +For Linux distribution .NET defines a process from which the .NET product can be built from source: [dotnet/source-build](https://github.com/dotnet/source-build). This process requires building the entire .NET product without any pre-built binaries. Some components which make up .NET target older frameworks which aren't built from the latest codebase. This is done because these components may need to run at design-time on a different framework (.NETFramework), or may also ship out-of-band as a NuGet package for use by libraries that work on many frameworks. To support building these components on past frameworks, source-build uses an alternate representation of reference assets for those frameworks: [dotnet/source-build-reference-packages](https://github.com/dotnet/source-build-reference-packages). In order to satisfy build-from-source, packages should only contain reference-API and metadata, no executable code. As a result, the source-build-reference-packages that represent past framework versions will omit source generators. This could be a problem for any component which needs to build during source-build and targets this old framework, for example a project targeting .NET 6.0 in the .NET 7.0 source build. That component would fail to build during source build due to the missing source-generator (see [Deterministic failure](#Deterministic%20failure)). That component can mitigate the failure by either including a .NET 7.0 configuration for source build. Alternatively, if the dependent source-generator is also out-of-band (for example: System.Text.Json) the failing component may reference the latest dependent out-of-band package to get the latest source-generator from the package instead of the ref-pack. To force components to make this mitigation early, we will make sure that Arcade disables source-generators from ref-packs which are not the latest when building for source build. From e69303f5023302d6e60ac2220a0ff2d819b69609 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Mon, 29 Mar 2021 13:23:26 -0700 Subject: [PATCH 13/16] Apply suggestions from code review Co-authored-by: Jared Parsons --- accepted/2021/InboxSourceGenerators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index c2ffc5864..02724f297 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -7,7 +7,7 @@ Roslyn source generators allow for component developers to provide functionality ## Scenarios and User Experience -A developer can use source generators for framework API without making any changes to their project. Creating a new project should be sufficient. +A developer can use source generators for framework API without making any changes to their project. Targeting the framework which exposes the source generator should be sufficient. A developer can use source generators for components in NuGet packages by referencing the NuGet package for that component. @@ -65,7 +65,7 @@ Alternative designs are listed in the [Q & A](#Q%20&%20A) section below. Source generators and analyzers are passed to the compiler as `@(Analyzer)` items. These items may come from NuGet packages, be built into the SDK targets, be directly defined by the user via `ProjectReference` or raw `Analyzer` items, or be provided by the new framework mechanism described in this document. All should be considered for conflict resolution based on the same constraint. -The .NET SDK already does conflict resolution for other asset types: references and copy-local runtime assets. It should do the same for source generators, or more generally Analyzers. Analyzers need not have "platform manifest" inputs for conflict resolution since all analyzers will be available during build, unlike runtime files which may not be available. Comparison for the sake of conflict resolution can consider the same assembly and file version rules as reference files. When finding two or more source generators with the same name conflict resolution will select the generator with higher assembly version, then higher file version, then that which is in the framework. This parallels the same rules followed by reference assemblies. The SDK may choose to optimize this comparison as it does for reference assemblies, by including a data file that includes analyzer metadata. +The .NET SDK already does conflict resolution for other asset types: references and copy-local runtime assets. It should do the same for source generators, or more generally analyzers. Analyzers need not have "platform manifest" inputs for conflict resolution since all analyzers will be available during build, unlike runtime files which may not be available. Comparison for the sake of conflict resolution can consider the same assembly and file version rules as reference files. When finding two or more source generators with the same name conflict resolution will select the generator with higher assembly version, then higher file version, then that which is in the framework. This parallels the same rules followed by reference assemblies. The SDK may choose to optimize this comparison as it does for reference assemblies, by including a data file that includes analyzer metadata. Source generator conflict resolution should run before compile, but after all NuGet package assets have been evaluated. Specific sequencing, public/private target naming, extensibility will not be specified here but should follow best practice as determined by the SDK team. From eea3e42d02930a1528c7c1cefe427507fa928b91 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 30 Mar 2021 12:55:28 -0700 Subject: [PATCH 14/16] Include some best-practice suggestions for implementing source generators And fix typos. --- accepted/2021/InboxSourceGenerators.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 02724f297..27367e494 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -55,7 +55,7 @@ Framework provided source generators should have a mechanism to be exposed and v Source generators and analyzers are represented and communicated in the same manner to the compiler, as analyzer dlls using the `-analyzer` switch. Though this document is motivated by describing the process for source generators it will work in the same manner for analyzers. In some places we may refer to the assemblies containing source generators as analyzers due to the way they are understood by the compiler, package manager, and SDK. -To facilitate this, we will package source generators inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [NuGet conventions for describing analyzers](https://docs.microsoft.com/en-us/nuget/guides/analyzers-conventions) with respect to the location in the package, however the SDK will not probe this path. All source generators and their metadata will be listed in FrameworkList.xml, similar to references. Analyzers will be added as `File` elements with `Type="Analyzer"` (constrasting to `Type="Managed"` which is currently used for references) and optionally `Language="cs|vb"`. Omitting the `Language` attribute means the analyzer applies to all languages. The .NET SDK will be responsible for locating the appropriate source generators from the refpack, based on the project's `$(Language)` and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other `@(Analyzer)` sources as described in the following section. When viewed from the IDE it should be clear that the source of the `Analyzer` is the framework reference, similarly to how it is clear that the source of NuGet package analyzers come from packaages. +To facilitate this, we will package source generators inside [reference packs](https://github.com/dotnet/designs/blob/main/accepted/2019/targeting-packs-and-runtime-packs.md). We will use the standard [NuGet conventions for describing analyzers](https://docs.microsoft.com/en-us/nuget/guides/analyzers-conventions) with respect to the location in the package, however the SDK will not probe this path. All source generators and their metadata will be listed in FrameworkList.xml, similar to references. Analyzers will be added as `File` elements with `Type="Analyzer"` (constrasting to `Type="Managed"` which is currently used for references) and optionally `Language="cs|vb|..."` (where languages match those honored by nuget conventions). Omitting the `Language` attribute means the analyzer applies to all languages. The .NET SDK will be responsible for locating the appropriate source generators from the refpack, based on the project's `$(Language)` and creating `@(Analyzer)` items as part of the `ResolveTargetingPackAssets` task. These items will be conflict-resolved with other `@(Analyzer)` sources as described in the following section. When viewed from the IDE it should be clear that the source of the `Analyzer` is the framework reference, similarly to how it is clear that the source of NuGet package analyzers come from packaages. Existing public functionality which controls the behavior of shared-framework references should also apply to source generators. `$(DisableImplicitFrameworkReferences)` should disable the creation of `@(Analyzer)` items. Any other property which might disable the creation of `@(Reference)` items from the ref-pack should also disable the creation of `@(Analyzer)` items. Additionally, a new property should be introduced to disable the creation of `@(Analyzer)` items: `$(DisableFrameworkReferenceAnalyzers)`. No mechanism will be provided to select individual `@(Analyzer)` items, just as there is no mechanism to select individual `@(Reference)` items. @@ -124,9 +124,9 @@ Source generator assemblies execute in the compiler process and must target .NET Source generators should limit their dependencies. There is no scheme for encoding framework-specific nor runtime-specific implementations of dependencies or source generators based on the runtime environment so source generators should avoid any dependencies on assemblies which need to differ by framework or runtime. Source generators may depend on packages which are known dependencies of the compiler and should depend on versions less than or equal to that provided by the compiler and should not include that dependency in their package. -Source generators should not rely on referencing nor executing framework types they wish to extend. Despite inbox source generators being coupled to a specific framework version, source generators cannot assume they will be running on that framework. They may be running on a newer, older, or completely different framework. For example, API added to help a source generator do its job may not be present when running on .NET Framework or the .NET Core version the compiler is running on. There is no type-equivalence guaranteed between types loaded in the compiler process and those of the user code that is under analysis. For example, a source generator cannot instantiate attribute instances in the compiler process from user references to those attributes in source. Should a source generator need to identify type usage it will need to examine user code references by name or resolve types in the user's references to ensure valid equivalence checks. Source sharing should be considered as an alternative for cases where source generators need to execute runtime library helper code when generating source. For example, serializer code which needs to examine the shape of user types can be shared between the runtime library and source generator. Common abstractions can be made over metadata to allow the same code to handle different sources of metadata (source vs reflection). +Source generators should not rely on referencing nor executing framework types they wish to extend. Despite inbox source generators being coupled to a specific framework version, source generators will not always run on that framework version. They may be running on a newer, older, or completely different framework. For example, API added to help a source generator do its job may not be present when running on .NET Framework or the .NET Core version the compiler is running on. There is no type-equivalence guaranteed between types loaded in the compiler process and those of the user code that is under analysis. For example, a source generator cannot instantiate attribute instances in the compiler process from user references to those attributes in source. Should a source generator need to identify type usage it will need to examine user code references by name or resolve types in the user's references to ensure valid equivalence checks. For examples see the [Source Generator Cookbook](https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md). Source sharing should be considered as an alternative for cases where source generators need to execute runtime library helper code when generating source. For example, serializer code which needs to examine the shape of user types can be shared between the runtime library and source generator. Common abstractions can be made over metadata to allow the same code to handle different sources of metadata (source vs reflection), -Source generators should localize any diagnostic messages displayed to the user. This can be done using localized resource satellite assemblies. These assemblies should be distributed with the source generator assembly. +Source generators should localize any diagnostic messages displayed to the user. This can be done using localized resource satellite assemblies. These assemblies should be distributed with the source generator assembly. Best practice for localizing generators or analyzers is to use [LocalizableResourceString](https://github.com/dotnet/roslyn/blob/7b9df38f26dae6296827f10cd6da5a36d5512604/src/Compilers/Core/Portable/Diagnostic/LocalizableResourceString.cs#L17), place resources in resx, and [enable Xliff tools in arcade](https://github.com/dotnet/arcade/blob/6cc4c1e9e23d5e65e88a8a57216b3d91e9b3d8db/src/Microsoft.DotNet.Arcade.Sdk/tools/Imports.targets#L26). ## Q & A @@ -136,15 +136,15 @@ Doing so would require source generators to carefully version and test across al ### Can't the analyzer handle delivering different behavior per library version? -We could implement the framework/library specific behavior in the analyzer itself, like the compiler does with `$(LangVersion)`. We could use an input like the project's `$(TargetFramework)`, metadata in the reference asssembly like a source generator version experessed in an attribute `AssemblyMetadataAttribute("UseMySourceGeneratorVersion", "1.2")`, or even do API probing to determine which features are supported by the library. Whichever input chosen would need to directly correlate with the beavior of the library selection (conflict resolution), ruling out `TargetFramework`, and be stable across servicing updates to provide determinsm to existing projects. These solutions introduce complexity into the source generator and risk for the end user. They would require us to build and test analyzers across all older targets: we would need to test the latest source generator across all previous versions of the library. They also introduce risk since they could impact a customer's application when using the latest SDK but targeting an older, LTS framework, since those would use the newest analyzer undergoing churn that is not scrutinized through our normal servicing process. +We could implement the framework/library specific behavior in the analyzer itself, like the compiler does with `$(LangVersion)`. We could use an input like the project's `$(TargetFramework)`, metadata in the reference asssembly like a source generator version experessed in an attribute `AssemblyMetadataAttribute("UseMySourceGeneratorVersion", "1.2")`, or even do API probing to determine which features are supported by the library. Whichever input chosen would need to directly correlate with the behavior of the library selection (conflict resolution), ruling out `TargetFramework`, and be stable across servicing updates to provide determinsm to existing projects. These solutions introduce complexity into the source generator and risk for the end user. They would require us to build and test analyzers across all older targets: we would need to test the latest source generator across all previous versions of the library. They also introduce risk since they could impact a customer's application when using the latest SDK but targeting an older, LTS framework, since those would use the newest analyzer undergoing churn that is not scrutinized through our normal servicing process. ### Inplace servicing updates are good enough for the compiler, why not source generators? The compiler has a very stable 1:1 relationship between a user's source and the code it generates. As such the user can control the output of the compiler through their own usage. The compiler's contribution of complexity to the user's assembly is very small and highly specified. -Contrast this to source generators which can take a very small amount of user source to produce a large amount of generated code. This large amount of generated code contains significant complexity which is loosely specified. Its behavior is specific to the component it extends. It's likely that we'd need to bugfix this code over time and change it as we develop features in the components it extends. It is also likely that we will learn new techniques and improvments to source generators over time, increasing the changes for bugfixes and features. +Contrast this to source generators which can take a very small amount of user source to produce a large amount of generated code. This large amount of generated code contains significant complexity which is loosely specified. Its behavior is specific to the component it extends. It's likely that we'd need to bugfix this code over time and change it as we develop features in the components it extends. It is also likely that we will learn new techniques and improvements to source generators over time, increasing the changes for bugfixes and features. -Users expect stability for projects which do not change, for example a project targeting an LTS release. It is much more difficult to ensure this stability when we need to significantly components which contribute complexity to that project. +Users expect stability for projects which do not change, for example a project targeting an LTS release. It is much more difficult to ensure this stability when we need to significantly change components which contribute complexity to that project. ### Isn't this a lot of servicing if we find a bug? From 193f4ff7bb3d903d6c7710fd5be50b8a2e6115bd Mon Sep 17 00:00:00 2001 From: Immo Landwerth Date: Sun, 25 Apr 2021 12:11:35 -0700 Subject: [PATCH 15/16] Use new owner marker --- accepted/2021/InboxSourceGenerators.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 27367e494..06b38eec3 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -1,7 +1,6 @@ # Inbox Source Generators -**PM** [Immo Landwerth](https://github.com/terrajobst) | -**Dev** [Eric StJohn](https://github.com/ericstj) +**Owner** [Eric StJohn](https://github.com/ericstj) Roslyn source generators allow for component developers to provide functionality that runs in the context of compilation, analyzes a user's code, and can generate additional code to add to the compilation. Compelling scenarios for source generators involve moving dynamic runtime code to be specialized and statically emitted by the compiler: specializing serializer code for a target type, pre-compiling regular expressions at design time, pre-computing composition graphs for DI systems, and many more. This technique was possible before but was rather clunky and had to be done outside the compiler, so its use was minimal and not part of the default experience. Roslyn source generators solve this and as a result we wish to develop many source generators which are part of the default experience for .NET applications. Source generators present a new type of challenge for a design time component: they need to be part of the toolchain but are tied to specific components within a target framework, and often re-implement or share the functionality of those components. We need to define how we ship these source generators and make them available to developers. We need to define the lifecycle for these source generators and how it is tied to the framework component it extends. From ad42ad182bba47da28ed5e572933a381266503f6 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Wed, 23 Jun 2021 23:10:22 -0700 Subject: [PATCH 16/16] Update index and add winforms --- INDEX.md | 1 + accepted/2021/InboxSourceGenerators.md | 1 + 2 files changed, 2 insertions(+) diff --git a/INDEX.md b/INDEX.md index c28679d0d..cc3a086b9 100644 --- a/INDEX.md +++ b/INDEX.md @@ -71,6 +71,7 @@ Use update-index to regenerate it: | 2020 | [Type for holding & converting binary data](accepted/2020/binary-data/binary-data.md) | [Immo Landwerth](https://github.com/terrajobst) | | 2021 | [.NET 6.0 Target Frameworks](accepted/2021/net6.0-tfms/net6.0-tfms.md) | [Immo Landwerth](https://github.com/terrajobst) | | 2021 | [Compile-time source generation for strongly-typed logging messages](accepted/2021/logging-generator.md) | [Maryam Ariyan](https://github.com/maryamariyan), [Martin Taillefer](https://github.com/geeknoid) | +| 2021 | [Inbox Source Generators](accepted/2021/InboxSourceGenerators.md) | [Eric StJohn](https://github.com/ericstj) | | 2021 | [Objective-C interoperability](accepted/2021/objectivec-interop.md) | [Aaron Robinson](https://github.com/AaronRobinsonMSFT) | | 2021 | [Preview Features](accepted/2021/preview-features/preview-features.md) | [Immo Landwerth](https://github.com/terrajobst) | | 2021 | [TFM for .NET nanoFramework](accepted/2021/nano-framework-tfm/nano-framework-tfm.md) | [Immo Landwerth](https://github.com/terrajobst), [Laurent Ellerbach](https://github.com/Ellerbach), [José Simões](https://github.com/josesimoes) | diff --git a/accepted/2021/InboxSourceGenerators.md b/accepted/2021/InboxSourceGenerators.md index 06b38eec3..72201d470 100644 --- a/accepted/2021/InboxSourceGenerators.md +++ b/accepted/2021/InboxSourceGenerators.md @@ -45,6 +45,7 @@ A developer has confidence that their library using inbox source generators will - Roslyn team - responsible for source generator infrastructure. - .NET SDK team - responsible for resolving shared frameworks and doing conflict resolution with content. - NuGet team - FYI only, no changes requested +- WindowsForms team - interested in providing source generators. ## Design