-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Source Generator #18
base: dev
Are you sure you want to change the base?
Source Generator #18
Conversation
Before I continue, can you comment on the naming and project structure? If everything looks file, I'll continue.
|
Ok, I'll look at it, as soon as I am done with my review of the Tests.. As a side note: Can't the tuples be fixed by just accepting a struct that has two fields and can implicitly be converted to and from a tuple???? |
Here's a quote from docs:
So, while an attribute can accept anything as a base Edit: Also, when I said basically done I meant the generator itself, I still need to migrate everything to be using the generator, though, I also want to test it in different conditions just in case, so if you want you can play around with it and tell me if you find any problems ;) Edit2: Edit3: A problem I run into while testing is that if we decorate a partial class that is spread into several files, having an attribute with Edit4: on my
Edit5: I'm also considering adding a small generator to generate Edit6: I think it would be easier to wait for the other 2 PRs to be properly merged before merging this, that way I could modify all 3 projects to use this generator. |
Since opening this PR, I've run into this amazing article on C# Source Generators by Andrew Lock, the creator of EnumGenerators. There he describes different methods to improve Source Generator performance, usability and testability, for example, a way to output generated files to be stored in version control (git) to compare/validate changes. I'm thinking about rewriting this PR when I have time. If you like the idea, I'll make it into a draft and work on it later? |
Yeah, that'd be great. I just came around (after a really long time) to finishing the unit-tests and implementing the updated Split methods as well as some new additions. Planning to release v1.4 with those features next week. PS: |
Goals:
Imlementation:
a new build target
DebugGenerators
is added that extends theDebug
target. This is so we can selectively callif (!Debugger.IsAttached) Debugger.Launch();
to debug the generator execution only when theDEBUGGENERATORS
symbol is defined. This is not the only way to debug generators, but it's the best one I know.a
publicinternal attribute by the nameGenerateCopyAttribute
is generated in theSpanExtensions.Enumerators
namespace. It can be used in the following way:The attribute accepts arrays of strings, one for normal string find and replaces, and another for regex patterns and replacements. It is not necessary to define both, but at least one of them has to be non-empty, otherwise a build error will be shown:
Ideally, the parameters should accept array of tuples of find&replace/pattern&replace, however .NET currently doesn't support that, thus the attribute accepts a normal array of strings, however, it assumes that every odd string is the find/pattern and every even string is a replacement, thus if the length of the array is not even, a build error is displayed:
If the marked class is a partial, the output generated file includes the original source file at the end of its name. This is to avoid collisions when a partial class is spread into multiple files and marked by the attribute in every file. This logic can still fail, for example, if the class is separated into files with the same name but in different subdirectories. Bcause of that, an additional parameter is avaliable,
GeneratedFileTag
, which will be used at the end of the output file name (instead of the original source file name), which can be used when a collision happens.The generator currently assumes that the same class in the same source file won't be marked by the attribute more than once. While this is an unlikely situation, it is nevertheless recommended to mention this in the documentation for future contributors.
The generator currently assumes that namespaces are not to be modified in the copy, thus the namespace and using statements are copied untouched. This may not be the case in the future, if it's decided to spread some implementations across namespaces, but for now it is ignored to simplify implementations.
Another thing that might be worth mentioning to future contributors is to pay attention to the replacement statements at the top of the class. For example, if the regex
(?<!ReadOnly)Span
->ReadOnlySpan
is used and a developer later adds a call to.AsSpan
, that will be replaced withAsReadOnlySpan
. In almost every case this can be solved by using a different approach (the developer might add an extension methodAsReadOnlySpan
in our example), or modifying the regex pattern ((?<!ReadOnly|As)Span
in our example), but is still something that should be kept in ming during development.