| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #ifndef CARBON_MIGRATE_CPP_REWRITER_H_
- #define CARBON_MIGRATE_CPP_REWRITER_H_
- #include <string>
- #include <utility>
- #include <variant>
- #include <vector>
- #include "clang/AST/ASTConsumer.h"
- #include "clang/AST/ASTTypeTraits.h"
- #include "clang/AST/RecursiveASTVisitor.h"
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Frontend/FrontendAction.h"
- #include "llvm/ADT/DenseMap.h"
- #include "migrate_cpp/output_segment.h"
- namespace Carbon {
- namespace Internal {
- struct Empty {
- friend auto operator==(Empty /*unused*/, Empty /*unused*/) -> bool {
- return true;
- }
- };
- struct Tombstone {
- friend auto operator==(Tombstone /*unused*/, Tombstone /*unused*/) -> bool {
- return true;
- }
- };
- // Type alias for the variant representing any of the values that can be
- // written with OutputWriter.
- using KeyType =
- std::variant<clang::DynTypedNode, clang::TypeLoc, Empty, Tombstone>;
- // `KeyInfo` is used as a template argument to `llvm::DenseMap` to specify how
- // to equality-compare and hash `KeyType`.
- struct KeyInfo {
- static auto isEqual(const KeyType& lhs, const KeyType& rhs) -> bool {
- return lhs == rhs;
- }
- static auto getHashValue(const KeyType& x) -> unsigned {
- return std::visit(
- [](auto x) -> unsigned {
- using Type = std::decay_t<decltype(x)>;
- if constexpr (std::is_same_v<Type, clang::DynTypedNode>) {
- return clang::DynTypedNode::DenseMapInfo::getHashValue(x);
- } else if constexpr (std::is_same_v<Type, clang::TypeLoc>) {
- // TODO: Improve this.
- return reinterpret_cast<uintptr_t>(x.getTypePtr());
- } else {
- return 0;
- }
- },
- x);
- }
- static auto getEmptyKey() -> KeyType { return Empty{}; }
- static auto getTombstoneKey() -> KeyType { return Tombstone{}; }
- };
- } // namespace Internal
- // `OutputWriter` is responsible for traversing the tree of `OutputSegment`s
- // and writing the correct data to its member `output`.
- struct OutputWriter {
- using SegmentMapType =
- llvm::DenseMap<Internal::KeyType, std::vector<OutputSegment>,
- Internal::KeyInfo>;
- auto Write(clang::SourceLocation loc, const OutputSegment& segment) const
- -> bool;
- const SegmentMapType& map;
- // Bounds represent the offsets into the primary file (multi-file refactorings
- // are not yet supported) that should be output. While primarily this is a
- // mechanism to make testing more robust, it can also be used to make local
- // changes to sections of C++ code.
- std::pair<size_t, size_t> bounds;
- clang::SourceManager& source_manager;
- std::string& output;
- };
- // `RewriteBuilder` is a recursive AST visitor. For each node, it computes and
- // stores a sequence of `OutputSegment`s describing how this node should be
- // replaced.
- class RewriteBuilder : public clang::RecursiveASTVisitor<RewriteBuilder> {
- public:
- using SegmentMapType = typename OutputWriter::SegmentMapType;
- // Constructs a `RewriteBuilder` which can read the AST from `context` and
- // will write results into `segments`.
- explicit RewriteBuilder(clang::ASTContext& context, SegmentMapType& segments)
- : context_(context), segments_(segments) {}
- // By default, traverse children nodes before their parent. Called by the CRTP
- // base class to determine traversal order.
- auto shouldTraversePostOrder() const -> bool { return true; }
- // Visitor member functions, defining how each node should be processed.
- auto VisitBuiltinTypeLoc(clang::BuiltinTypeLoc type_loc) -> bool;
- auto VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr* expr) -> bool;
- auto VisitDeclRefExpr(clang::DeclRefExpr* expr) -> bool;
- auto VisitDeclStmt(clang::DeclStmt* stmt) -> bool;
- auto VisitImplicitCastExpr(clang::ImplicitCastExpr* expr) -> bool;
- auto VisitIntegerLiteral(clang::IntegerLiteral* expr) -> bool;
- auto VisitParmVarDecl(clang::ParmVarDecl* decl) -> bool;
- auto VisitPointerTypeLoc(clang::PointerTypeLoc type_loc) -> bool;
- auto VisitReturnStmt(clang::ReturnStmt* stmt) -> bool;
- auto VisitTranslationUnitDecl(clang::TranslationUnitDecl* decl) -> bool;
- auto VisitUnaryOperator(clang::UnaryOperator* expr) -> bool;
- auto TraverseFunctionDecl(clang::FunctionDecl* decl) -> bool;
- auto TraverseVarDecl(clang::VarDecl* decl) -> bool;
- auto segments() const -> const SegmentMapType& { return segments_; }
- auto segments() -> SegmentMapType& { return segments_; }
- private:
- // Associates `output_segments` in the output map `this->segments()` with the
- // key `node`, so as to declare that, when output is being written, `node`
- // should be replaced with the sequence of outputs described by
- // `output_segments`.
- auto SetReplacement(clang::DynTypedNode node,
- std::vector<OutputSegment> output_segments) -> void {
- segments_.try_emplace(node, std::move(output_segments));
- }
- auto SetReplacement(clang::TypeLoc node,
- std::vector<OutputSegment> output_segments) -> void {
- segments_.try_emplace(node, std::move(output_segments));
- }
- template <typename T>
- auto SetReplacement(const T* node, std::vector<OutputSegment> output_segments)
- -> void {
- segments_.try_emplace(clang::DynTypedNode::create(*node),
- std::move(output_segments));
- }
- // Invokes the overload of `SetReplacement` defined above. Equivalent to
- // `this->SetReplacement(node, std::vector<OutputSegment>(1, segment))`.
- template <typename T>
- auto SetReplacement(const T* node, OutputSegment segment) -> void {
- std::vector<OutputSegment> node_segments;
- node_segments.push_back(std::move(segment));
- SetReplacement(node, std::move(node_segments));
- }
- auto SetReplacement(clang::TypeLoc type_loc, OutputSegment segment) -> void {
- std::vector<OutputSegment> node_segments;
- node_segments.push_back(std::move(segment));
- SetReplacement(type_loc, std::move(node_segments));
- }
- // Returns a `llvm::StringRef` into the source text corresponding to the
- // half-open interval starting at `begin` (inclusive) and ending at `end`
- // (exclusive).
- auto TextFor(clang::SourceLocation begin, clang::SourceLocation end) const
- -> llvm::StringRef;
- // Returns a `llvm::StringRef` into the source text for the single token
- // located at `loc`.
- auto TextForTokenAt(clang::SourceLocation loc) const -> llvm::StringRef;
- clang::ASTContext& context_;
- SegmentMapType& segments_;
- };
- // An `ASTConsumer` which, when executed, populates a `std::string` with the
- // text of a Carbon source file which is a best approximation of the
- // semantics of the corresponding C++ translation unit defined by the consumed
- // AST.
- class MigrationConsumer : public clang::ASTConsumer {
- public:
- explicit MigrationConsumer(std::string& result,
- std::pair<size_t, size_t> output_range)
- : result_(result), output_range_(std::move(output_range)) {}
- auto HandleTranslationUnit(clang::ASTContext& context) -> void override;
- private:
- RewriteBuilder::SegmentMapType segment_map_;
- std::string& result_;
- std::pair<size_t, size_t> output_range_;
- };
- // An `ASTFrontendAction` which constructs a `MigrationConsumer` and invokes it
- // on an AST, populating a `std::string` with the text of a Carbon source file
- // which is a best approximation of the semantics of the corresponding C++
- // translation unit defined by the consumed AST.
- class MigrationAction : public clang::ASTFrontendAction {
- public:
- // Constructs the `MigrationAction`. The parameter `result` is a reference to
- // the `std::string` where output will be written. Only output corresponding
- // to text at offsets that fall in between `output_range.first` and
- // `output_range.second` will be written.
- explicit MigrationAction(std::string& result,
- std::pair<size_t, size_t> output_range)
- : result_(result), output_range_(std::move(output_range)) {}
- // Returns a `std::unique_ptr` to a `clang::MigrationConsumer` which populates
- // the output `result`.
- auto CreateASTConsumer(clang::CompilerInstance& /*CI*/,
- llvm::StringRef /*InFile*/)
- -> std::unique_ptr<clang::ASTConsumer> override {
- return std::make_unique<MigrationConsumer>(result_, output_range_);
- }
- private:
- std::string& result_;
- std::pair<size_t, size_t> output_range_;
- };
- } // namespace Carbon
- #endif // CARBON_MIGRATE_CPP_REWRITER_H_
|