{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/5561","repository_url":"https://api.github.com/repos/dotnet/roslyn","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/5561/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/5561/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/5561/events","html_url":"https://github.com/dotnet/roslyn/issues/5561","id":108981236,"number":5561,"title":"[Proposal] enable code generating extensions to the compiler","user":{"login":"mattwar","id":3387305,"avatar_url":"https://avatars.githubusercontent.com/u/3387305?v=3","gravatar_id":"","url":"https://api.github.com/users/mattwar","html_url":"https://github.com/mattwar","followers_url":"https://api.github.com/users/mattwar/followers","following_url":"https://api.github.com/users/mattwar/following{/other_user}","gists_url":"https://api.github.com/users/mattwar/gists{/gist_id}","starred_url":"https://api.github.com/users/mattwar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattwar/subscriptions","organizations_url":"https://api.github.com/users/mattwar/orgs","repos_url":"https://api.github.com/users/mattwar/repos","events_url":"https://api.github.com/users/mattwar/events{/privacy}","received_events_url":"https://api.github.com/users/mattwar/received_events","type":"User","site_admin":false},"labels":[{"id":167974279,"url":"https://api.github.com/repos/dotnet/roslyn/labels/3%20-%20Working","name":"3 - Working","color":"CCCCCC","default":false},{"id":166673288,"url":"https://api.github.com/repos/dotnet/roslyn/labels/Area-Compilers","name":"Area-Compilers","color":"5319e7","default":false},{"id":169838071,"url":"https://api.github.com/repos/dotnet/roslyn/labels/Area-Language%20Design","name":"Area-Language Design","color":"5319e7","default":false},{"id":166677091,"url":"https://api.github.com/repos/dotnet/roslyn/labels/Feature%20Request","name":"Feature Request","color":"fc2929","default":false},{"id":365157692,"url":"https://api.github.com/repos/dotnet/roslyn/labels/New%20Language%20Feature%20-%20Replace/Original","name":"New Language Feature - Replace/Original","color":"bfd4f2","default":false}],"state":"open","locked":false,"assignee":{"login":"cston","id":10732005,"avatar_url":"https://avatars.githubusercontent.com/u/10732005?v=3","gravatar_id":"","url":"https://api.github.com/users/cston","html_url":"https://github.com/cston","followers_url":"https://api.github.com/users/cston/followers","following_url":"https://api.github.com/users/cston/following{/other_user}","gists_url":"https://api.github.com/users/cston/gists{/gist_id}","starred_url":"https://api.github.com/users/cston/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cston/subscriptions","organizations_url":"https://api.github.com/users/cston/orgs","repos_url":"https://api.github.com/users/cston/repos","events_url":"https://api.github.com/users/cston/events{/privacy}","received_events_url":"https://api.github.com/users/cston/received_events","type":"User","site_admin":false},"assignees":[{"login":"cston","id":10732005,"avatar_url":"https://avatars.githubusercontent.com/u/10732005?v=3","gravatar_id":"","url":"https://api.github.com/users/cston","html_url":"https://github.com/cston","followers_url":"https://api.github.com/users/cston/followers","following_url":"https://api.github.com/users/cston/following{/other_user}","gists_url":"https://api.github.com/users/cston/gists{/gist_id}","starred_url":"https://api.github.com/users/cston/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cston/subscriptions","organizations_url":"https://api.github.com/users/cston/orgs","repos_url":"https://api.github.com/users/cston/repos","events_url":"https://api.github.com/users/cston/events{/privacy}","received_events_url":"https://api.github.com/users/cston/received_events","type":"User","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/dotnet/roslyn/milestones/22","html_url":"https://github.com/dotnet/roslyn/milestone/22","labels_url":"https://api.github.com/repos/dotnet/roslyn/milestones/22/labels","id":1886693,"number":22,"title":"2.1","description":"","creator":{"login":"ManishJayaswal","id":9527491,"avatar_url":"https://avatars.githubusercontent.com/u/9527491?v=3","gravatar_id":"","url":"https://api.github.com/users/ManishJayaswal","html_url":"https://github.com/ManishJayaswal","followers_url":"https://api.github.com/users/ManishJayaswal/followers","following_url":"https://api.github.com/users/ManishJayaswal/following{/other_user}","gists_url":"https://api.github.com/users/ManishJayaswal/gists{/gist_id}","starred_url":"https://api.github.com/users/ManishJayaswal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ManishJayaswal/subscriptions","organizations_url":"https://api.github.com/users/ManishJayaswal/orgs","repos_url":"https://api.github.com/users/ManishJayaswal/repos","events_url":"https://api.github.com/users/ManishJayaswal/events{/privacy}","received_events_url":"https://api.github.com/users/ManishJayaswal/received_events","type":"User","site_admin":false},"open_issues":679,"closed_issues":22,"state":"open","created_at":"2016-07-15T21:14:40Z","updated_at":"2016-12-21T23:04:41Z","due_on":null,"closed_at":null},"comments":173,"created_at":"2015-09-29T23:35:42Z","updated_at":"2016-12-22T08:34:11Z","closed_at":null,"body":"Often when writing software, we find ourselves repeatedly typing similar logic over and over again, each time just different enough from the last to make generalizing it into an API impractical. We refer to this type of code as boilerplate, the code we have to write around the actual logic we want to have, just to make it work with the language and environment that we use.\n\nOne way of avoiding writing boilerplate code, is to have the computer generate it for us. After all, computers are really good at that sort of thing.  But in order for the computer to generate code for us it has to have some input to base it on.  Typical code generators are design-time tools that we work with outside of our codebase, that generate source that we include with it. These tools usually prefer their input to be XML or JSON files that we either manipulate manually or have some WSIWYG editor that lets us drag, drop and click it into existence.  Other tools are build-time, that get run by our build system just before our project is built, but they too are driven by external inputs like XML and JSON files that we must manipulate separately from our code.\n\nThese solutions have their merits, but they are often intrusive, requiring us to structure our code in particular ways that allow the merging of the generated code to work well with what we’ve written.  The biggest drawback, is that these tools require entire facets of our codebase to be defined in another language outside of the code we use to write our primary logic.\n\nSome solutions, like post-build rewriters, do a little better in the regard, because they operate directly on the code we’ve written, adding new logic into the assembly directly.  However, they too have their drawbacks. For instance, post-build rewriters can never introduce new types and API’s for our code to reference, because they come too late in the process. So they can only change the code we wrote to do something else.  Even worse, assembly rewriters are very difficult to build because they must work at the level of the IL or assembly language, doing the heavy lifting to re-derive the context of our code that was lost during compilation, and to generate new code as IL and metadata without the luxury of having a compiler to do it.  For most folks, choosing this technique to build tools to reduce boilerplate code is typically a non-starter.\n\nYet the biggest sin of all, is that all of these solutions require us to manipulate our nearly unfathomable build system, and in fact requires us to have a build system in the first place, and who really wants to do that.  Am I Right?\n## Proposal:  Code Injectors\n\nCode injectors are source code generators that are extensions to the compiler you are using, as you are using it. When the compiler in instructed to compile the source code you wrote, code injectors are given a chance to exam your code and add new code that gets compiled in along with it.\n\nWhen you type your code into an editor or IDE, the compiler can be engaged to provide feedback that includes the new code added by the code generators. Thus, it is possible to have the compiler respond to your work and introduce new code as you type that you can directly make use of.  \n\nYou write a code injector similarly to how you write a C# and VB diagnostic analyzer today.  You may choose to think of code injectors as analyzers that instead of reporting new diagnostics after examining the source code, augment the source code by adding new declarations.\n\nYou define a class in an assembly that gets loaded by the compiler when it is run to compiler your code.  This could easily be the same assembly you have used to supply analyzers.  This class is initialized by the compiler with a context that you can use to register callbacks into your code when particular compilation events occur.\n\nFor example, ignoring namespaces for a moment, this contrived code injector gives every class defined in source a new constant field called ClassName that is a string containing the name of the class.\n\n``` cs\n[CodeInjector(LanguageNames.CSharp)]\npublic class MyInjector : CodeInjector\n{\n    public override void Initialize(InitializationContext context)\n    {\n        context.RegisterSymbolAction(InjectCodeForSymbol);\n    }\n\n    public void InjectCodeForSymbol(SymbolInjectionContext context)\n    {\n        if (context.Symbol.TypeKind == TypeKind.Class)\n        {\n            context.AddCompilationUnit($@”partial class {context.Symbol.Name} \n                      {{ public const string ClassName = “”{context.Symbol.Name}””; }}”);\n        }\n    }\n}\n```\n\nThis works because of the existence of the C# and VB partial class language feature.  \nOf course, not all code injectors need to be in the business of adding members to the classes you wrote, or especially not adding members to all the classes you wrote indiscriminately.  Code injectors can add entirely new declarations, new types and API’s that are meant to simply be used by your code, not to modify your code.\n\nYet, the prospect of having code injectors modify the code you wrote enables many compelling scenarios that wouldn’t be possible otherwise.  A companion proposal for the C# and VB languages #5292 introduces a new feature that makes it possible to have code generators not only add new declarations/members to your code, but also to augment the methods and properties you wrote too.  \n\nNow, you can get rid of boilerplate logic like all that INotifyPropertyChanged code you need just to make data binding work.  (Or this that so last decade that I need a better example?)\n## Subjects not covered in this proposal but open for discussion too\n1.  Ordering of Injectors – this concerns the order of injectors, which are run first etc., and the order of the new sources as presented to the compiler.  The is of interest to the supersedes feature proposed in #5292\n2.  Callback events – beyond callbacks for type symbols declared in source, what other callback patterns would be useful for code generators, keeping in mind that these will likely need to be invoked by the IDE as well.\n3.  Having multiple injection event handlers lead to the generation of the same source code, only once, and being smart about it.\n4.  Recursion - Can generated code trigger additional code injection events for the newly injected declarations?  I’d rather the answer be No, since this will make the system much simpler.\n5.  More?\n"},"comment":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/comments/268747038","html_url":"https://github.com/dotnet/roslyn/issues/5561#issuecomment-268747038","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/5561","id":268747038,"user":{"login":"davidfowl","id":95136,"avatar_url":"https://avatars.githubusercontent.com/u/95136?v=3","gravatar_id":"","url":"https://api.github.com/users/davidfowl","html_url":"https://github.com/davidfowl","followers_url":"https://api.github.com/users/davidfowl/followers","following_url":"https://api.github.com/users/davidfowl/following{/other_user}","gists_url":"https://api.github.com/users/davidfowl/gists{/gist_id}","starred_url":"https://api.github.com/users/davidfowl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/davidfowl/subscriptions","organizations_url":"https://api.github.com/users/davidfowl/orgs","repos_url":"https://api.github.com/users/davidfowl/repos","events_url":"https://api.github.com/users/davidfowl/events{/privacy}","received_events_url":"https://api.github.com/users/davidfowl/received_events","type":"User","site_admin":false},"created_at":"2016-12-22T08:34:11Z","updated_at":"2016-12-22T08:34:11Z","body":"> Another option we discussed was to simply move this out of the core compiler. Instead move it to say the MSBuild pipeline. The same generator experience can be defined in MSBuild but it doesn't have the same expectations around an IDE experience.\r\n\r\nI would start with that but still have a hook at the csc level (similar to analyzers). That makes it run at the \"right\" time. This is essentially what compile modules did (ignored the IDE 😄 because it was hard.).\r\n"}}
{"actor":{"display_login":"davidfowl"}}