Files
carbon-lang/migrate_cpp/rewriter.h
T
Clayton GearhartandClayton Gearhart 3628e22ca2 Put python object definition and assignment on same line (#3057)
Where there was an object initialization immediately followed by
assignment I condensed it to one line, which seems to be the convention
looking at other files. I also deleted the repetition of 'of' in some
c++ comments because it was grammatically incorrect.

---------

Co-authored-by: Clayton Gearhart <claytongearhart240@gmail.com>
2023-08-14 07:38:02 +00:00

224 lines
8.4 KiB
C++

// 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_