Files
carbon-lang/migrate_cpp/rewriter.h
T
Jon Ross-Perkins 4d522c8e90 Finish making clang-tidy (mostly) work (again) and run -fix (#2312)
This does some more work to the run_clang_tidy.py wrapper script, and runs an example pass.

"again" because it's really the proto fuzzer changes that broke it, it had been working before.

"mostly" because there's still an issue within the proto fuzzer that it can't find "port/protobuf.h", i.e. https://github.com/google/libprotobuf-mutator/tree/master/port, but I'm still hesitant to add an include path there.
2022-10-18 15:48:17 -07: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 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 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_