Skip to content

Commit fd28f5f

Browse files
authored
Add the beginnings of a RecursiveASTVisitor-based migration tool. (carbon-language#2041)
The approach uses a RecursiveASTVisitor rather than matchers. Matchers and callbacks do not compose neatly and introduce significant runtime overhead over RecursiveASTVisitor when an action needs to be performed on most nodes.
1 parent 7935728 commit fd28f5f

File tree

6 files changed

+770
-0
lines changed

6 files changed

+770
-0
lines changed

migrate_cpp/BUILD

+39
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,42 @@ py_binary(
1111
],
1212
python_version = "PY3",
1313
)
14+
15+
cc_library(
16+
name = "output_segment",
17+
hdrs = ["output_segment.h"],
18+
deps = [
19+
"//common:check",
20+
"@llvm-project//clang:ast",
21+
],
22+
)
23+
24+
cc_library(
25+
name = "rewriter",
26+
srcs = ["rewriter.cpp"],
27+
hdrs = ["rewriter.h"],
28+
deps = [
29+
":output_segment",
30+
"//common:check",
31+
"@llvm-project//clang:ast",
32+
"@llvm-project//clang:basic",
33+
"@llvm-project//clang:frontend",
34+
"@llvm-project//clang:lex",
35+
"@llvm-project//clang:tooling",
36+
"@llvm-project//clang:tooling_core",
37+
"@llvm-project//llvm:Support",
38+
],
39+
)
40+
41+
cc_test(
42+
name = "rewriter_test",
43+
srcs = ["rewriter_test.cpp"],
44+
deps = [
45+
":rewriter",
46+
"//common:gtest_main",
47+
"@com_google_googletest//:gtest",
48+
"@llvm-project//clang:ast",
49+
"@llvm-project//clang:frontend",
50+
"@llvm-project//clang:tooling",
51+
],
52+
)

migrate_cpp/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,18 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
1111
## Table of contents
1212

1313
- [Overview](#overview)
14+
- [Structure](#structure)
1415

1516
<!-- tocstop -->
1617

1718
## Overview
1819

1920
`migrate_cpp` assists in migration of C++ code to Carbon. It's currently being
2021
assembled; more documentation will be added later.
22+
23+
## Structure
24+
25+
The `migrate_cpp` tool uses a `clang::RecursiveASTVisitor` to traverse Clang's
26+
AST and, to each node, associate replacements. Each node's replacement is a
27+
sequence of text, or a reference to some other node that should be used to
28+
replace it.

migrate_cpp/output_segment.h

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
2+
// Exceptions. See /LICENSE for license information.
3+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
#ifndef CARBON_MIGRATE_CPP_OUTPUT_SEGMENT_H_
6+
#define CARBON_MIGRATE_CPP_OUTPUT_SEGMENT_H_
7+
8+
#include <string>
9+
#include <type_traits>
10+
#include <utility>
11+
#include <variant>
12+
13+
#include "clang/AST/ASTTypeTraits.h"
14+
#include "common/check.h"
15+
16+
namespace Carbon {
17+
18+
// Represents a segment of the output string. `OutputSegment`s come in two
19+
// flavors: Text and Node. A text segment holds string text that should be used
20+
// to be added to the output. A node segment holds a node in Clang's AST and
21+
// indicates that the output associated to that node should be the output
22+
// segment that the `RewriteBuilder` (defined below) has attached to that AST
23+
// node.
24+
//
25+
// For example, the output for a binary operator node corresponding to the C++
26+
// code snippet `f() + 3 * 5`, would be the sequence of three output segments:
27+
//
28+
// {Node(lhs), Text(" + "), Node(rhs)}
29+
//
30+
// The left-hand side and right-hand side can then be queried recursively to
31+
// determine what their output should be.
32+
class OutputSegment {
33+
public:
34+
// Returns whether or not the type T is an acceptable node type from which an
35+
// OutputSegment can be constructed. We intentionally do not want to support
36+
// `clang::Type` because we support traversing through `clang::TypeLoc`
37+
// instead. However, most other types we intend to support as they become
38+
// necessary.
39+
template <typename T>
40+
static constexpr bool IsSupportedClangASTNodeType() {
41+
return std::is_convertible_v<T*, clang::Stmt*> ||
42+
std::is_convertible_v<T*, clang::Decl*>;
43+
}
44+
45+
// Creates a text-based `OutputSegment`.
46+
explicit OutputSegment(std::string content) : content_(std::move(content)) {}
47+
explicit OutputSegment(llvm::StringRef content) : content_(content.str()) {}
48+
explicit OutputSegment(const char* content) : content_(content) {}
49+
50+
// Creates a node-based `OutputSegment` from `node`.
51+
explicit OutputSegment(const clang::DynTypedNode& node) : content_(node) {}
52+
template <typename T,
53+
std::enable_if_t<OutputSegment::IsSupportedClangASTNodeType<T>(),
54+
int> = 0>
55+
explicit OutputSegment(const T* node);
56+
57+
// Creates a TypeLoc-based `OutputSegment` from `type_loc`.
58+
explicit OutputSegment(clang::TypeLoc type_loc)
59+
: content_(PassThroughQualifiedTypeLoc(type_loc)) {}
60+
61+
private:
62+
friend struct OutputWriter;
63+
64+
template <typename T>
65+
T& AssertNotNull(T* ptr) {
66+
CARBON_CHECK(ptr != nullptr);
67+
return *ptr;
68+
}
69+
70+
// Traversals for TypeLocs have some sharp corners. In particular,
71+
// QualifiedTypeLocs are silently passed through to their unqualified part.
72+
// This means that when constructing output segments we also need to match
73+
// this behavior.
74+
static auto PassThroughQualifiedTypeLoc(clang::TypeLoc type_loc)
75+
-> clang::TypeLoc {
76+
auto qtl = type_loc.getAs<clang::QualifiedTypeLoc>();
77+
return qtl.isNull() ? type_loc : qtl.getUnqualifiedLoc();
78+
}
79+
80+
std::variant<std::string, clang::DynTypedNode, clang::TypeLoc> content_;
81+
};
82+
83+
template <typename T, std::enable_if_t<
84+
OutputSegment::IsSupportedClangASTNodeType<T>(), int>>
85+
OutputSegment::OutputSegment(const T* node)
86+
: content_(clang::DynTypedNode::create(AssertNotNull(node))) {}
87+
88+
} // namespace Carbon
89+
90+
#endif // CARBON_MIGRATE_CPP_OUTPUT_SEGMENT_H_

0 commit comments

Comments
 (0)