Getting started
Installation
Getting the tool
The Bronto refactoring tool is currently in early access. Please contact us if you are interested in trying it out on your codebase. If you just want to play around with the API, you can skip the installation step and try it on Compiler Explorer.
Support library
You'll need the the support library, which can be found on our GitHub repository brontosource/bronto. The support library uses the 0BSD license and compiles to nothing at runtime, so it won't impact your existing licensing, binary size, or performance.
The support library is header-only and can be easily compiled with most build systems. We provide builtin support for
- CMake 3.10.0+
- Bazel 8.1.1+
Hello, Bronto!
These steps walk you through a simple usage of the Bronto refactoring tool,
catching a potential performance issue. Specifically, we will look for places
where a std::string
is compared for equality with ""
, when the empty
member function would be more efficient.
Write the following C++ program, and save it as "hello.cc"
.
#include <bronto/bronto.hpp>
#include <string>
#include <iostream>
struct Rewrite : bronto::rewrite_expr {
BRONTO_BEFORE()
void EqualsEmptyString(std::string s) {
return s == "";
}
BRONTO_AFTER()
void Replacement(std::string s) {
return s.empty();
}
};
int main(int argc, char *argv[]) {
if (argc != 2) { return 1; }
std::string user = argv[1];
if (user == "") { user = "Charlotte Bronto"; }
std::cout << "Hello, " << user << "!\n";
return 0;
}
Run the tool
bronto-refactor edit -- clang -xc++ -c hello.cc
Reopen "hello.cc"
. You should see
#include <bronto/bronto.hpp>
#include <string>
#include <iostream>
struct Rewrite : bronto::rewrite_expr {
BRONTO_BEFORE()
void EqualsEmptyString(std::string s) {
return s == "";
}
BRONTO_AFTER()
void Replacement(std::string s) {
return s.empty();
}
};
int main(int argc, char *argv[]) {
if (argc != 2) { return 1; }
std::string user = argv[1];
if (user.empty()) { user = "Charlotte Bronto"; }
std::cout << "Hello, " << user << "!\n";
return 0;
}
Running the tool
The bronto-refactor
has two modes: edit
and patch
.
edit
will apply all changes in place, modifying any relevant files.patch
will create a patch file with the changes.
bronto-refactor <MODE> -- "$COMPILATION_ARGUMENTS_ARRAY[@]"
Specifying refactorings
Bronto provides two overarching utilities for specifying refactorings: Inlining and pattern-matching.
Inlining functions
Inlining is the idea of replacing a reference to a symbol with its definition.
For functions, this means replacing every call to the function with whatever
the body of the function does. You can annotate a function with the
BRONTO_INLINE()
macro to tell Bronto that the annotated function should be
inlined. For example, consider the following deprecation:
template <typename T>
class Container {
public:
T pop() { ... }
bool empty() const { ... }
...
};
// Deprecated. Use the `empty` member function instead.
BRONTO_INLINE()
bool IsEmpty(const Container& c) {
return c.empty();
}
The BRONTO_INLINE()
attribute indicates that any call to IsEmpty
should be
replaced with the empty
member function on Container
. Bronto understands C++
syntax, and will produce intelligent diffs. For instance, running Bronto with the above annotations produces the following diff (removing operator*
and using operator->
):
void safe_pop(const Container<std::string>* container_ptr) {
+ if (container_ptr && !IsEmpty(*container_ptr)) {
- if (container_ptr && !container_ptr->empty()) {
container_ptr->pop();
}
}
Bronto also supports annotating member functions, operators, function templates, and constructors.
Inlining types
Bronto supports inlining types with the same BRONTO_INLINE()
macro
applied to using declarations.
template <typename T>
using Point3d BRONTO_INLINE() = std::array<T, 3>;
Any uses of Point3d
, either in a variable declaration or in another type will
be properly replaced.
Expression pattern matching
Bronto can also find and replace more general patterns than simple function calls. For example, you can optimize string concatenation by avoiding unneeded conversions with the rule:
struct OperatorPlusToStrCat : bronto::rewrite_expr {
template <std::integral T>
BRONTO_BEFORE()
auto operator_plus(std::string lhs, T rhs) {
return lhs + std::to_string(rhs));
}
template <typename T>
BRONTO_AFTER()
auto use_strcat(std::string lhs, T rhs) {
return absl::StrCat(lhs, rhs);
}
};
To use this form of pattern matching, a struct needs three things:
- It must inherit from
bronto::rewrite_expr
- A pattern to look for, defined as a member function annotated with
BRONTO_BEFORE()
- A pattern to be used as the replacement, defined as a member function
annotated with with
BRONTO_BEFORE()