https://github.com/goldsborough/clang-expand Skip to content Toggle navigation Sign up * Product + Actions Automate any workflow + Packages Host and manage packages + Security Find and fix vulnerabilities + Codespaces Instant dev environments + Copilot Write better code with AI + Code review Manage code changes + Issues Plan and track work + Discussions Collaborate outside of code Explore + All features + Documentation + GitHub Skills + Blog * Solutions For + Enterprise + Teams + Startups + Education By Solution + CI/CD & Automation + DevOps + DevSecOps Resources + Customer Stories + White papers, Ebooks, Webinars + Partners * Open Source + GitHub Sponsors Fund open source developers + The ReadME Project GitHub community articles Repositories + Topics + Trending + Collections * Pricing Search or jump to... Search code, repositories, users, issues, pull requests... Search [ ] Clear Search syntax tips Provide feedback We read every piece of feedback, and take your input very seriously. [ ] [ ] Include my email address so I can be contacted Cancel Submit feedback Saved searches Use saved searches to filter your results more quickly Name [ ] Query [ ] To see all available qualifiers, see our documentation. Cancel Create saved search Sign in Sign up You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. Dismiss alert {{ message }} goldsborough / clang-expand Public * Notifications * Fork 22 * Star 203 A clang tool for happy refactoring without source-code gymnastics License MIT license 203 stars 22 forks Activity Star Notifications * Code * Issues 5 * Pull requests 1 * Actions * Projects 0 * Security * Insights More * Code * Issues * Pull requests * Actions * Projects * Security * Insights goldsborough/clang-expand This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. master Switch branches/tags [ ] Branches Tags Could not load branches Nothing to show {{ refName }} default View all branches Could not load tags Nothing to show {{ refName }} default View all tags Name already in use A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch? Cancel Create 2 branches 1 tag Code * Local * Codespaces * Clone HTTPS GitHub CLI [https://github.com/g] Use Git or checkout with SVN using the web URL. [gh repo clone goldsb] Work fast with our official CLI. Learn more about the CLI. * Open with GitHub Desktop * Download ZIP Sign In Required Please sign in to use Codespaces. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching Xcode If nothing happens, download Xcode and try again. Launching Visual Studio Code Your codespace will open once ready. There was a problem preparing your codespace, please try again. Latest commit @goldsborough goldsborough Update README.md ... e1e8dd1 Mar 29, 2017 Update README.md e1e8dd1 Git stats * 142 commits Files Permalink Failed to load latest commit information. Type Name Latest commit message Commit time cmake/modules s/VERBOSE_CONFIG/FIND_LLVM_VERBOSE_CONFIG March 23, 2017 00:40 docker Updated opensuse dockerfile March 23, 2017 13:02 docs More progress on documentation March 16, 2017 22:35 extra Update README.md March 18, 2017 23:07 include/clang-expand Added license header to every file March 22, 2017 03:21 source Added license header to every file March 22, 2017 03:21 third-party Switched to JSON instead of YAML March 17, 2017 01:07 .clang-format Updated README.md March 18, 2017 23:06 .clang-tidy Fixed some clang-tidy warnings March 17, 2017 03:19 .gitignore Adding dockerfiles and target March 22, 2017 00:26 CMakeLists.txt Updated opensuse dockerfile March 23, 2017 13:02 CPPLINT.cfg Fixed issue with call range extent for binary operators March 13, 2017 22:10 LICENSE Initial commit February 25, 2017 21:31 README.md Update README.md March 29, 2017 10:01 clang-expand.cpp Added license header to every file March 22, 2017 03:21 docker-compose.yaml Moved docker-compose out of docker/ folder March 22, 2017 02:58 View code [ ] clang-expand Overview Usage Example editor integration Limitations Building Docker Windows Documentation License Authors README.md clang-expand [clang-expa] A clang tool for happy refactoring without source-code gymnastics. license Docker Pulls Github All Releases GitHub release Overview I recently overheard the following conversation on my way to work, that may seem familiar to you: Gandalf: It is important to refactor your code and keep functions concise and coherent. Harry Potter: Yeah, sure, but I hate having to jump around between files to get the full picture of what my code is doing. One function = one place to look. Obi Wan Kenobi: Use the force, Harry. Batman: He means clang-expand Inspired by Gandalf's words, I set out to find a solution to Harry's problem and built clang-expand. Point it at a function invocation in your source code and tell it where to look for stuff, and it will find the correct definition of that particular (template) function, method, operator overload or even constructor and "expand" it into the current scope. Expanding means it will: 1. Replace parameters with respective argument expressions. That is, for a function f(int x) that you call with f(5), clang-expand will rewrite every occurrence of x inside f to 5. Note that since clang-expand uses clang, it actually understands C++ and knows what occurrences of x are parameter references and what aren't. Default arguments are replaced as well. Given [ template void magic(Range& range, int meaning_of_life = 42) { auto iterator = std::find(range.begin(), range.end(), meaning_of_life); if (iterator != range.end()) { range.erase(iterator); std::cout << "Successfully erased all meaning of life\n"; } } ] Unexpanded Expanded [ [ std::vector v = {1, 42, 3}; std::vector v = {1, 42, 3}; magic(v); auto iterator = std::find(v.begin(), v.end(), 42); ^ if (iterator != v.end()) { std::cout << "Successfully erased all meaning of life\n"; ] v.erase(iterator); } ] As you can see, clang-expand actually instantiated the template function during the expansion. This is because on the level that it operates on within the clang AST, semantic analysis, including template type deduction, are already complete. This means that calling templates is not a problem for clang-expand. 2. If you're assigning the return value of a function you expand to a variable, clang-expand will replace every return statement inside the function with an assignment. It attempts to do this in a reasonably intelligent way, constructing the variable with the return value directly if there is only one return and else first declaring the variable and then assigning. The latter only works if the type of the variable is default-constructible and clang-expand will refuse to expand otherwise. Given [ std::string concat(const std::string& first, const std::string& second) { return first + "-"s + second; } std::string concat(const std::string& first, const std::string& second, bool kebab) { if (kebab) { return first + "-"s + second; } else { return first + std::toupper(second.front(), {}) + second.substr(1); } } ] Unexpanded Expanded [ [ auto kebab = concat("clang", "expand"); std::string kebab = "clang" + "-"s + "expand"; ^ ] ] Unexpanded Expanded [ [ auto maybeCamel = concat("clang", "expand", flipCoin()); std::string maybeCamel; ^ if (flipCoin()) { maybeCamel = "clang" + "-"s + "expand"; ] } else { maybeCamel = "clang" + std::toupper("expand".front(), {}) + "expand".substr(1); } ] 3. If you're calling a method, clang-expand will prepend the base to every method or member of referenced inside: Unexpanded Expanded^1 [ [ std::vector my_vec; std::vector my_vec; my_vec.emplace_back(42); if (my_vec.__end_ < my_vec.__end_cap()) ^ { __RAII_IncreaseAnnotator __annotator(*this); ] __alloc_traits::construct(my_vec.__alloc(), _VSTD::__to_raw_pointer(my_vec.__end_), _VSTD::forward<_Args>(42)...); __annotator.__done(); ++my_vec.__end_; } else my_vec.__emplace_back_slow_path(_VSTD::forward<_Args>(42)...); ] ^1 This is the implementation on my system, of course. 4. If the function you're expanding is an operator, clang-expand can handle that just as well: Given [ struct by_lightning { bool operator==(const by_lightning& other) const noexcept { return this->circuit == other.circuit; } short circuit; }; ] Unexpanded Expanded [ [ by_lightning first{1}; by_lightning first{1}; by_lightning second{2}; by_lightning second{2}; return first == second; return first.circuit == other.circuit; ^ ] ] 5. Besides expanding functions that are real code, clang-expand can also expand function-like and even object-like macros (that just #define something without being parameterized): Given [ #define MAX(a, b) (a) > (b) ? (a) : (b) #define PI 3.14 ] Unexpanded Expanded [ [ double pi_if_a_greater_b(double a, double b) { double pi_if_a_greater_b(double a, double b) { auto greater = MAX(a, b); auto greater = a > b ? a : b; ^ if (greater == a) { if (greater == a) { return 3.14; return PI; } ^ return -1; } } return -1; } ] ] 6. clang-expand not only performs substitution for function parameters, it can also substitute type and non-type template parameters! Voila: Given [ template auto my_template(Deduced deduced) { using Alias = Explicit; return deduced + static_cast(number); } ] Unexpanded Expanded [ [ my_template(10); using Alias = float; ^ return 10 + static_cast(24); ] ] Usage clang-expand is implemented as a command-line tool targeted at building editor integrations. The tool itself has the following help text (excerpt): $ clang-expand -help USAGE: clang-expand [options] [... ] OPTIONS: clang-expand options: -call - Whether to return the source range of the call -column= - The column number of the function to expand -declaration - Whether to return the original declaration -definition - Whether to return the original definition -file= - The source file of the function to expand -line= - The line number of the function to expand -rewrite - Whether to generate the rewritten (expanded) definition Basically, you have to pass it any sources you want the tool to look for definitions in as positional arguments. The -file, -line and -column options then determine the location of the call to expand. This position has to be somewhere on the token you want to expand (i.e. not necessarily at the beginning). The -file defaults to the first source if you omit the option. Additionally, you have to pass any options required to compile the files at the end, following --. For example, given: foo.h: int foo(); foo.cpp: int foo() { return 42; } main.cpp: #include "foo.h" auto main() -> int { auto x = foo(); } The following command would do the job: $ clang-expand main.cpp foo.cpp -line=3 -column=14 -- -I/path/to/include -std=c++14 which will output: { "call": { "begin": { "column": 3, "line": 3 }, "end": { "column": 17, "line": 3 } }, "declaration": { "location": { "filename": "/path/to/foo.h", "offset": { "column": 5, "line": 1 } }, "name": "foo", "text": "int foo();" }, "definition": { "location": { "filename": "/path/to/foo.cpp", "offset": { "column": 5, "line": 1 } }, "macro": false, "rewritten": "int x = 42;", "text": "int foo() { return 42; }" } } Yes, JSON! But why so much output? Well, since clang-expand has to find the declaration and definition of a function you want to expand, it might as well also return the location and full text for either (because why not). As such, clang-expand can also be used as a backend for "go-to-definition"/"show-declaration" functions inside a text editor (though an indexed-solution like ctags is likely faster for just that). For expanding, what's most interesting here is the call section and the definition.rewritten field. The former is the entire range (defined by two (line, column) pairs) in the source code that you'll want to replace with the expansion. The latter is the text to insert instead. Even though the overhead to grab information about the definition and declaration is negligible compared to the entire operation, it may still be beneficial to turn off retrieval of certain parts of what clang-expand outputs, or you may simply not need some of the output. This is the case when you're only interested in expanding for example, where you only need the call and definition section. For this reason, the clang-expand tool takes boolean -call, -declaration, -definition and -rewrite options. By default, these flags are all set to true, i.e. all of these sections will be included. By setting them to -