blob: 62ec4418d5811e00ac307f3bbcb6f80f8a6b4a27 [file] [log] [blame] [view]
andybons6eaa0c0d2015-08-26 20:12:521# Clang Tool Refactoring
andybons3322f762015-08-24 21:37:092
andybons6eaa0c0d2015-08-26 20:12:523[TOC]
andybons3322f762015-08-24 21:37:094
dchengce2375e2016-01-12 01:09:075## Introduction
6
7Clang tools can help with global refactorings of Chromium code. Clang tools can
8take advantage of clang's AST to perform refactorings that would be impossible
9with a traditional find-and-replace regexp:
10
11* Constructing `scoped_ptr<T>` from `NULL`: <https://crbug.com/173286>
12* Implicit conversions of `scoped_refptr<T>` to `T*`: <https://crbug.com/110610>
13* Rename everything in Blink to follow Chromium style: <https://crbug.com/563793>
jdoerrie8c5b8252017-10-14 06:28:5814* Clean up of deprecated `base::Value` APIs: <https://crbug.com/581865>
dchengce2375e2016-01-12 01:09:0715
andybons6eaa0c0d2015-08-26 20:12:5216## Caveats
andybons3322f762015-08-24 21:37:0917
Daniel Cheng69413be2017-10-24 01:23:5718* Invocations of a clang tool runs on on only one build config at a time. For
19example, running the tool across a `target_os="win"` build won't update code
20that is guarded by `OS_POSIX`. Performing a global refactoring will often
21require running the tool once for each build config.
andybons6eaa0c0d2015-08-26 20:12:5222
23## Prerequisites
24
dchengce2375e2016-01-12 01:09:0725A Chromium checkout created with `fetch` should have everything needed.
andybons6eaa0c0d2015-08-26 20:12:5226
dchengce2375e2016-01-12 01:09:0727For convenience, add `third_party/llvm-build/Release+Asserts/bin` to `$PATH`.
andybons6eaa0c0d2015-08-26 20:12:5228
dchengce2375e2016-01-12 01:09:0729## Writing the tool
andybons6eaa0c0d2015-08-26 20:12:5230
dchengce2375e2016-01-12 01:09:0731LLVM uses C++11 and CMake. Source code for Chromium clang tools lives in
Daniel Cheng69413be2017-10-24 01:23:5732[//tools/clang]. It is generally easiest to use one of the already-written tools
33as the base for writing a new tool.
andybons6eaa0c0d2015-08-26 20:12:5234
dchengce2375e2016-01-12 01:09:0735Chromium clang tools generally follow this pattern:
36
Daniel Cheng69413be2017-10-24 01:23:57371. Instantiate a
38 [`clang::ast_matchers::MatchFinder`][clang-docs-match-finder].
392. Call `addMatcher()` to register
40 [`clang::ast_matchers::MatchFinder::MatchCallback`][clang-docs-match-callback]
41 actions to execute when [matching][matcher-reference] the AST.
dchengce2375e2016-01-12 01:09:07423. Create a new `clang::tooling::FrontendActionFactory` from the `MatchFinder`.
434. Run the action across the specified files with
Daniel Cheng69413be2017-10-24 01:23:5744 [`clang::tooling::ClangTool::run`][clang-docs-clang-tool-run].
455. Serialize generated [`clang::tooling::Replacement`][clang-docs-replacement]s
danakjef9f1fa2016-01-16 00:37:2846 to `stdout`.
dchengce2375e2016-01-12 01:09:0747
48Other useful references when writing the tool:
49
Daniel Cheng69413be2017-10-24 01:23:5750* [Clang doxygen reference][clang-docs]
51* [Tutorial for building tools using LibTooling and
52 LibASTMatchers][clang-tooling-tutorial]
dchengce2375e2016-01-12 01:09:0753
54### Edit serialization format
55```
56==== BEGIN EDITS ====
Lukasz Anforowicze4d0e92f2020-05-06 20:47:1257r:::path/to/file/to/edit:::offset1:::length1:::replacement text
58r:::path/to/file/to/edit:::offset2:::length2:::replacement text
59r:::path/to/file2/to/edit:::offset3:::length3:::replacement text
60include-user-header:::path/to/file2/to/edit:::-1:::-1:::header/file/to/include.h
dchengce2375e2016-01-12 01:09:0761
62 ...
63
64==== END EDITS ====
andybons3322f762015-08-24 21:37:0965```
66
dchengce2375e2016-01-12 01:09:0767The header and footer are required. Each line between the header and footer
68represents one edit. Fields are separated by `:::`, and the first field must
Lukasz Anforowicze4d0e92f2020-05-06 20:47:1269be `r` (for replacement) or `include-user-header`.
70A deletion is an edit with no replacement text.
andybons6eaa0c0d2015-08-26 20:12:5271
lukaszaf9b89e72016-12-28 19:43:0672The edits are applied by [`apply_edits.py`](#Running), which understands certain
dchengce2375e2016-01-12 01:09:0773conventions:
74
lukaszaf9b89e72016-12-28 19:43:0675* The clang tool should munge newlines in replacement text to `\0`. The script
dchengce2375e2016-01-12 01:09:0776 knows to translate `\0` back to newlines when applying edits.
77* When removing an element from a 'list' (e.g. function parameters,
lukaszaf9b89e72016-12-28 19:43:0678 initializers), the clang tool should emit a deletion for just the element.
79 The script understands how to extend the deletion to remove commas, etc. as
dchengce2375e2016-01-12 01:09:0780 needed.
81
82TODO: Document more about `SourceLocation` and how spelling loc differs from
83expansion loc, etc.
84
85### Why not RefactoringTool?
danakjef9f1fa2016-01-16 00:37:2886While clang has a [`clang::tooling::RefactoringTool`](http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1RefactoringTool.html)
87to automatically apply the generated replacements and save the results, it
88doesn't work well for Chromium:
dchengce2375e2016-01-12 01:09:0789
Daniel Cheng82f80d62017-05-18 05:39:3890* Clang tools run actions serially, so run time scales poorly to tens of
dchengce2375e2016-01-12 01:09:0791 thousands of files.
92* A parsing error in any file (quite common in NaCl source) prevents any of
93 the generated replacements from being applied.
94
95## Building
96Synopsis:
danakjef9f1fa2016-01-16 00:37:2897
andybons6eaa0c0d2015-08-26 20:12:5298```shell
Hans Wennborgd1eec552019-05-02 14:59:0799tools/clang/scripts/build.py --bootstrap --without-android \
dchengf2390712017-01-05 06:41:45100 --extra-tools rewrite_to_chrome_style
dchengce2375e2016-01-12 01:09:07101```
danakjef9f1fa2016-01-16 00:37:28102
Daniel Cheng69413be2017-10-24 01:23:57103Running this command builds the [Oilpan plugin][//tools/clang/blink_gc_plugin],
104the [Chrome style plugin][//tools/clang/plugins], and the [Blink to Chrome style
105rewriter][//tools/clang/rewrite_to_chrome_style]. Additional arguments to
106`--extra-tools` should be the name of subdirectories in [//tools/clang].
dchengce2375e2016-01-12 01:09:07107
danakj30d0f8c92016-01-28 00:26:33108It is important to use --bootstrap as there appear to be [bugs](https://crbug.com/580745)
109in the clang library this script produces if you build it with gcc, which is the default.
110
vabr9ed3f432017-06-09 07:30:42111Once clang is bootsrapped, incremental builds can be done by invoking `ninja` in
112the `third_party/llvm-build/Release+Asserts` directory. In particular,
113recompiling solely the tool you are writing can be accomplished by executing
114`ninja rewrite_to_chrome_style` (replace `rewrite_to_chrome_style` with your
115tool's name).
116
dchengce2375e2016-01-12 01:09:07117## Running
qyearsleyc0dc6f42016-12-02 22:13:39118First, build all Chromium targets to avoid failures due to missing dependencies
dchengce2375e2016-01-12 01:09:07119that are generated as part of the build:
danakjef9f1fa2016-01-16 00:37:28120
dchengce2375e2016-01-12 01:09:07121```shell
danakjca6b31b52016-12-22 22:05:53122ninja -C out/Debug # For non-Windows
123ninja -d keeprsp -C out/Debug # For Windows
lukaszaf9b89e72016-12-28 19:43:06124
125# experimental alternative:
Yannic Bonenberger748bb0d2018-07-02 21:10:06126$gen_targets = $(ninja -C out/Debug -t targets all \
lukaszaf9b89e72016-12-28 19:43:06127 | grep '^gen/[^: ]*\.[ch][pc]*:' \
Yannic Bonenberger748bb0d2018-07-02 21:10:06128 | cut -f 1 -d :)
lukaszaf9b89e72016-12-28 19:43:06129ninja -C out/Debug $gen_targets
danakjca6b31b52016-12-22 22:05:53130```
131
lukaszaf9b89e72016-12-28 19:43:06132Then run the actual clang tool to generate a list of edits:
Daniel Cheng9ce2a302016-01-16 01:17:57133
134```shell
Daniel Cheng51c55302017-05-04 00:39:16135tools/clang/scripts/run_tool.py --tool <path to tool> \
dchengce2375e2016-01-12 01:09:07136 --generate-compdb
Daniel Cheng51c55302017-05-04 00:39:16137 -p out/Debug <path 1> <path 2> ... >/tmp/list-of-edits.debug
dchengce2375e2016-01-12 01:09:07138```
andybons6eaa0c0d2015-08-26 20:12:52139
dchengce2375e2016-01-12 01:09:07140`--generate-compdb` can be omitted if the compile DB was already generated and
141the list of build flags and source files has not changed since generation.
142
143`<path 1>`, `<path 2>`, etc are optional arguments to filter the files to run
lukaszaf9b89e72016-12-28 19:43:06144the tool against. This is helpful when sharding global refactorings into smaller
dchengce2375e2016-01-12 01:09:07145chunks. For example, the following command will run the `empty_string` tool
lukaszaf9b89e72016-12-28 19:43:06146against just the `.c`, `.cc`, `.cpp`, `.m`, `.mm` files in `//net`. Note that
147the filtering is not applied to the *output* of the tool - the tool can emit
148edits that apply to files outside of `//cc` (i.e. edits that apply to headers
149from `//base` that got included by source files in `//cc`).
dchengce2375e2016-01-12 01:09:07150
151```shell
Daniel Cheng51c55302017-05-04 00:39:16152tools/clang/scripts/run_tool.py --tool empty_string \
Yannic Bonenberger748bb0d2018-07-02 21:10:06153 --generate-compdb \
Daniel Cheng51c55302017-05-04 00:39:16154 -p out/Debug net >/tmp/list-of-edits.debug
dchengce2375e2016-01-12 01:09:07155```
156
lukaszaf9b89e72016-12-28 19:43:06157Note that some header files might only be included from generated files (e.g.
158from only from some `.cpp` files under out/Debug/gen). To make sure that
159contents of such header files are processed by the clang tool, the clang tool
160needs to be run against the generated files. The only way to accomplish this
161today is to pass `--all` switch to `run_tool.py` - this will run the clang tool
162against all the sources from the compilation database.
163
164Finally, apply the edits as follows:
165
166```shell
167cat /tmp/list-of-edits.debug \
168 | tools/clang/scripts/extract_edits.py \
Daniel Cheng51c55302017-05-04 00:39:16169 | tools/clang/scripts/apply_edits.py -p out/Debug <path 1> <path 2> ...
lukaszaf9b89e72016-12-28 19:43:06170```
171
172The apply_edits.py tool will only apply edits to files actually under control of
173`git`. `<path 1>`, `<path 2>`, etc are optional arguments to further filter the
174files that the edits are applied to. Note that semantics of these filters is
175distinctly different from the arguments of `run_tool.py` filters - one set of
176filters controls which files are edited, the other set of filters controls which
177files the clang tool is run against.
178
dchengce2375e2016-01-12 01:09:07179## Debugging
180Dumping the AST for a file:
Daniel Cheng9ce2a302016-01-16 01:17:57181
andybons6eaa0c0d2015-08-26 20:12:52182```shell
Daniel Cheng69413be2017-10-24 01:23:57183clang++ -Xclang -ast-dump -std=c++14 foo.cc | less -R
andybons3322f762015-08-24 21:37:09184```
185
dchengce2375e2016-01-12 01:09:07186Using `clang-query` to dynamically test matchers (requires checking out
Daniel Cheng69413be2017-10-24 01:23:57187and building [clang-tools-extra][]):
Daniel Cheng9ce2a302016-01-16 01:17:57188
andybons6eaa0c0d2015-08-26 20:12:52189```shell
dchengce2375e2016-01-12 01:09:07190clang-query -p path/to/compdb base/memory/ref_counted.cc
andybons3322f762015-08-24 21:37:09191```
192
dchengce2375e2016-01-12 01:09:07193`printf` debugging:
Daniel Cheng9ce2a302016-01-16 01:17:57194
dchengce2375e2016-01-12 01:09:07195```c++
196 clang::Decl* decl = result.Nodes.getNodeAs<clang::Decl>("decl");
197 decl->dumpColor();
198 clang::Stmt* stmt = result.Nodes.getNodeAs<clang::Stmt>("stmt");
199 stmt->dumpColor();
andybons3322f762015-08-24 21:37:09200```
Daniel Cheng9ce2a302016-01-16 01:17:57201
dchengce2375e2016-01-12 01:09:07202By default, the script hides the output of the tool. The easiest way to change
203that is to `return 1` from the `main()` function of the clang tool.
andybons6eaa0c0d2015-08-26 20:12:52204
205## Testing
dchengce2375e2016-01-12 01:09:07206Synposis:
Daniel Cheng9ce2a302016-01-16 01:17:57207
andybons6eaa0c0d2015-08-26 20:12:52208```shell
Ramin Halavatieb3f807a2017-08-02 05:31:31209tools/clang/scripts/test_tool.py <tool name> [--apply-edits]
andybons3322f762015-08-24 21:37:09210```
andybons6eaa0c0d2015-08-26 20:12:52211
dchengce2375e2016-01-12 01:09:07212The name of the tool binary and the subdirectory for the tool in
213`//tools/clang` must match. The test runner finds all files that match the
Ramin Halavatieb3f807a2017-08-02 05:31:31214pattern `//tools/clang/<tool name>/tests/*-original.cc`, and runs the tool
215across those files.
216If `--apply-edits` switch is presented, tool outputs are applied to respective
217files and compared to the `*-expected.cc` version. If there is a mismatch, the
218result is saved in `*-actual.cc`.
219When `--apply-edits` switch is not presented, tool outputs are compared to
220`*-expected.txt` and if different, the result is saved in `*-actual.txt`. Note
221that in this case, only one test file is expected.
Daniel Cheng69413be2017-10-24 01:23:57222
223[//tools/clang]: https://chromium.googlesource.com/chromium/src/+/master/tools/clang/
224[clang-docs-match-finder]: http://clang.llvm.org/doxygen/classclang_1_1ast__matchers_1_1MatchFinder.html
225[clang-docs-match-callback]: http://clang.llvm.org/doxygen/classclang_1_1ast__matchers_1_1MatchFinder_1_1MatchCallback.html
226[matcher-reference]: http://clang.llvm.org/docs/LibASTMatchersReference.html
227[clang-docs-clang-tool-run]: http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1ClangTool.html#acec91f63b45ac7ee2d6c94cb9c10dab3
228[clang-docs-replacement]: http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1Replacement.html
229[clang-docs]: http://clang.llvm.org/doxygen/index.html
230[clang-tooling-tutorial]: http://clang.llvm.org/docs/LibASTMatchersTutorial.html
231[//tools/clang/blink_gc_plugin]: https://chromium.googlesource.com/chromium/src/+/master/tools/clang/blink_gc_plugin/
232[//tools/clang/plugins]: https://chromium.googlesource.com/chromium/src/+/master/tools/clang/plugins/
233[//tools/clang/rewrite_to_chrome_style]: https://chromium.googlesource.com/chromium/src/+/master/tools/clang/rewrite_to_chrome_style/
234[clang-tools-extra]: (https://github.com/llvm-mirror/clang-tools-extra)