This change adds a Metadata field to ASTEdit, Edit, and AtomicChange so that edits can have associated metadata and that metadata can be constructed with Transformer-based RewriteRules. Metadata is ignored when applying edits to source, but other consumers of AtomicChange can use this metadata to direct how they want to consume each edit.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Looks good! Only real question is one of design -- should we consider the (deeper) change of templating the various types rather than using dynamic typing? For that matter, the decision doesn't even have to be the same for both AtomicChange and the Transformer types. Transformer could be templated while AtomicChange uses any.
I'm inclined towards what you have, but that may just be laziness on part (since this requires minimal changes). I'm curious to hear what Dmitri thinks.
clang/include/clang/Tooling/Refactoring/AtomicChange.h | ||
---|---|---|
144 | Please add a field comment. Something like what you have in the revision description would be good. | |
clang/lib/Tooling/Refactoring/AtomicChange.cpp | ||
210 | I assume that : AtomicChange(SM, KeyPosition), Metadata(std::move(M)) {} didn't work? | |
clang/unittests/Tooling/RefactoringTest.cpp | ||
1299 | any reason not to use int directly as the type carried in the metadata? | |
clang/unittests/Tooling/TransformerTest.cpp | ||
460 | Why expect vs assert? |
I think the tradeoff here is
Dynamic typing -- faster compile times, type safety checked at run-time (in tests), lower maintenance cost
Templates -- Faster runtime, type safety checked at compile-time, better user expereience
I don't think the runtime speed is particularly important here in the sense that it's unlikely to be the bottleneck. Given the crash-on-invalid-type semantics, we're essentially trading off usability for maintenance cost. In this case I don't think asking users to do the extra cast is a significant enough burden to warrant the cost of templating everything.
I also don't strongly object to it. The nice thing about llvm::Any, is that this can be mostly changed incrementally if we later decide that's appropriate (only having to change the one getMetadata call at the call-site).
clang/lib/Tooling/Refactoring/AtomicChange.cpp | ||
---|---|---|
210 | Correct. You can't use a forwarding constructor followed by a field initializer (which I learned by attempt it on this PR). | |
clang/unittests/Tooling/TransformerTest.cpp | ||
460 | EXPECT when the test can reasonably continue to produce useful output without crashing. So here, if the tool fails, we can still safely access Changes, but if Changes.size() != 1, we can't safely index into it. |
For me, the more important part of the tradeoff is whether we will have one type or multiple. If we use Any, then all AtomicChange regardless of what produced them, will have the same type. If we use templates, then AtomicChange<T> is a different type from AtomicChange<U>. So based on that I think using Any is a better choice, since infrastructure code would want to handle with AtomicChange objects and not have to be implemented as a template over arbitrary metadata type.
Regarding the metadata itself, WDYT about using a map from string to any instead of just one any? This way multiple layers of the system would be able to attach metadata without interfering.
I'm convinced (both yours and Andy's arguments). Andy, please consider whether any of this belongs in comments. (I don't have much opinion).
Regarding the metadata itself, WDYT about using a map from string to any instead of just one any? This way multiple layers of the system would be able to attach metadata without interfering.
Sounds good, but I'm fine either way.
I'm not against a map, but I don't see the need for it right now. We don't have multiple stages that write to the same AtomicChange in any tool I'm aware of. Given that these are immutable after construction, I think just the Any is simpler. So long as we're willing to break things later (Does LLVM have a policy on API compatibility?) I think it's better to start with the simpler thing.
Please add a field comment. Something like what you have in the revision description would be good.