blob: d4e9f94748c8347b700a7fa074e060944baf2d62 [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 ====
57r:::path/to/file1:::offset1:::length1:::replacement text
58r:::path/to/file2:::offset2:::length2:::replacement text
59
60 ...
61
62==== END EDITS ====
andybons3322f762015-08-24 21:37:0963```
64
dchengce2375e2016-01-12 01:09:0765The header and footer are required. Each line between the header and footer
66represents one edit. Fields are separated by `:::`, and the first field must
67be `r` (for replacement). In the future, this may be extended to handle header
68insertion/removal. A deletion is an edit with no replacement text.
andybons6eaa0c0d2015-08-26 20:12:5269
lukaszaf9b89e72016-12-28 19:43:0670The edits are applied by [`apply_edits.py`](#Running), which understands certain
dchengce2375e2016-01-12 01:09:0771conventions:
72
lukaszaf9b89e72016-12-28 19:43:0673* The clang tool should munge newlines in replacement text to `\0`. The script
dchengce2375e2016-01-12 01:09:0774 knows to translate `\0` back to newlines when applying edits.
75* When removing an element from a 'list' (e.g. function parameters,
lukaszaf9b89e72016-12-28 19:43:0676 initializers), the clang tool should emit a deletion for just the element.
77 The script understands how to extend the deletion to remove commas, etc. as
dchengce2375e2016-01-12 01:09:0778 needed.
79
80TODO: Document more about `SourceLocation` and how spelling loc differs from
81expansion loc, etc.
82
83### Why not RefactoringTool?
danakjef9f1fa2016-01-16 00:37:2884While clang has a [`clang::tooling::RefactoringTool`](http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1RefactoringTool.html)
85to automatically apply the generated replacements and save the results, it
86doesn't work well for Chromium:
dchengce2375e2016-01-12 01:09:0787
Daniel Cheng82f80d62017-05-18 05:39:3888* Clang tools run actions serially, so run time scales poorly to tens of
dchengce2375e2016-01-12 01:09:0789 thousands of files.
90* A parsing error in any file (quite common in NaCl source) prevents any of
91 the generated replacements from being applied.
92
93## Building
94Synopsis:
danakjef9f1fa2016-01-16 00:37:2895
andybons6eaa0c0d2015-08-26 20:12:5296```shell
Hans Wennborgd1eec552019-05-02 14:59:0797tools/clang/scripts/build.py --bootstrap --without-android \
dchengf2390712017-01-05 06:41:4598 --extra-tools rewrite_to_chrome_style
dchengce2375e2016-01-12 01:09:0799```
danakjef9f1fa2016-01-16 00:37:28100
Daniel Cheng69413be2017-10-24 01:23:57101Running this command builds the [Oilpan plugin][//tools/clang/blink_gc_plugin],
102the [Chrome style plugin][//tools/clang/plugins], and the [Blink to Chrome style
103rewriter][//tools/clang/rewrite_to_chrome_style]. Additional arguments to
104`--extra-tools` should be the name of subdirectories in [//tools/clang].
dchengce2375e2016-01-12 01:09:07105
danakj30d0f8c92016-01-28 00:26:33106It is important to use --bootstrap as there appear to be [bugs](https://crbug.com/580745)
107in the clang library this script produces if you build it with gcc, which is the default.
108
vabr9ed3f432017-06-09 07:30:42109Once clang is bootsrapped, incremental builds can be done by invoking `ninja` in
110the `third_party/llvm-build/Release+Asserts` directory. In particular,
111recompiling solely the tool you are writing can be accomplished by executing
112`ninja rewrite_to_chrome_style` (replace `rewrite_to_chrome_style` with your
113tool's name).
114
dchengce2375e2016-01-12 01:09:07115## Running
qyearsleyc0dc6f42016-12-02 22:13:39116First, build all Chromium targets to avoid failures due to missing dependencies
dchengce2375e2016-01-12 01:09:07117that are generated as part of the build:
danakjef9f1fa2016-01-16 00:37:28118
dchengce2375e2016-01-12 01:09:07119```shell
danakjca6b31b52016-12-22 22:05:53120ninja -C out/Debug # For non-Windows
121ninja -d keeprsp -C out/Debug # For Windows
lukaszaf9b89e72016-12-28 19:43:06122
123# experimental alternative:
Yannic Bonenberger748bb0d2018-07-02 21:10:06124$gen_targets = $(ninja -C out/Debug -t targets all \
lukaszaf9b89e72016-12-28 19:43:06125 | grep '^gen/[^: ]*\.[ch][pc]*:' \
Yannic Bonenberger748bb0d2018-07-02 21:10:06126 | cut -f 1 -d :)
lukaszaf9b89e72016-12-28 19:43:06127ninja -C out/Debug $gen_targets
danakjca6b31b52016-12-22 22:05:53128```
129
lukaszaf9b89e72016-12-28 19:43:06130Then run the actual clang tool to generate a list of edits:
Daniel Cheng9ce2a302016-01-16 01:17:57131
132```shell
Daniel Cheng51c55302017-05-04 00:39:16133tools/clang/scripts/run_tool.py --tool <path to tool> \
dchengce2375e2016-01-12 01:09:07134 --generate-compdb
Daniel Cheng51c55302017-05-04 00:39:16135 -p out/Debug <path 1> <path 2> ... >/tmp/list-of-edits.debug
dchengce2375e2016-01-12 01:09:07136```
andybons6eaa0c0d2015-08-26 20:12:52137
dchengce2375e2016-01-12 01:09:07138`--generate-compdb` can be omitted if the compile DB was already generated and
139the list of build flags and source files has not changed since generation.
140
141`<path 1>`, `<path 2>`, etc are optional arguments to filter the files to run
lukaszaf9b89e72016-12-28 19:43:06142the tool against. This is helpful when sharding global refactorings into smaller
dchengce2375e2016-01-12 01:09:07143chunks. For example, the following command will run the `empty_string` tool
lukaszaf9b89e72016-12-28 19:43:06144against just the `.c`, `.cc`, `.cpp`, `.m`, `.mm` files in `//net`. Note that
145the filtering is not applied to the *output* of the tool - the tool can emit
146edits that apply to files outside of `//cc` (i.e. edits that apply to headers
147from `//base` that got included by source files in `//cc`).
dchengce2375e2016-01-12 01:09:07148
149```shell
Daniel Cheng51c55302017-05-04 00:39:16150tools/clang/scripts/run_tool.py --tool empty_string \
Yannic Bonenberger748bb0d2018-07-02 21:10:06151 --generate-compdb \
Daniel Cheng51c55302017-05-04 00:39:16152 -p out/Debug net >/tmp/list-of-edits.debug
dchengce2375e2016-01-12 01:09:07153```
154
lukaszaf9b89e72016-12-28 19:43:06155Note that some header files might only be included from generated files (e.g.
156from only from some `.cpp` files under out/Debug/gen). To make sure that
157contents of such header files are processed by the clang tool, the clang tool
158needs to be run against the generated files. The only way to accomplish this
159today is to pass `--all` switch to `run_tool.py` - this will run the clang tool
160against all the sources from the compilation database.
161
162Finally, apply the edits as follows:
163
164```shell
165cat /tmp/list-of-edits.debug \
166 | tools/clang/scripts/extract_edits.py \
Daniel Cheng51c55302017-05-04 00:39:16167 | tools/clang/scripts/apply_edits.py -p out/Debug <path 1> <path 2> ...
lukaszaf9b89e72016-12-28 19:43:06168```
169
170The apply_edits.py tool will only apply edits to files actually under control of
171`git`. `<path 1>`, `<path 2>`, etc are optional arguments to further filter the
172files that the edits are applied to. Note that semantics of these filters is
173distinctly different from the arguments of `run_tool.py` filters - one set of
174filters controls which files are edited, the other set of filters controls which
175files the clang tool is run against.
176
dchengce2375e2016-01-12 01:09:07177## Debugging
178Dumping the AST for a file:
Daniel Cheng9ce2a302016-01-16 01:17:57179
andybons6eaa0c0d2015-08-26 20:12:52180```shell
Daniel Cheng69413be2017-10-24 01:23:57181clang++ -Xclang -ast-dump -std=c++14 foo.cc | less -R
andybons3322f762015-08-24 21:37:09182```
183
dchengce2375e2016-01-12 01:09:07184Using `clang-query` to dynamically test matchers (requires checking out
Daniel Cheng69413be2017-10-24 01:23:57185and building [clang-tools-extra][]):
Daniel Cheng9ce2a302016-01-16 01:17:57186
andybons6eaa0c0d2015-08-26 20:12:52187```shell
dchengce2375e2016-01-12 01:09:07188clang-query -p path/to/compdb base/memory/ref_counted.cc
andybons3322f762015-08-24 21:37:09189```
190
dchengce2375e2016-01-12 01:09:07191`printf` debugging:
Daniel Cheng9ce2a302016-01-16 01:17:57192
dchengce2375e2016-01-12 01:09:07193```c++
194 clang::Decl* decl = result.Nodes.getNodeAs<clang::Decl>("decl");
195 decl->dumpColor();
196 clang::Stmt* stmt = result.Nodes.getNodeAs<clang::Stmt>("stmt");
197 stmt->dumpColor();
andybons3322f762015-08-24 21:37:09198```
Daniel Cheng9ce2a302016-01-16 01:17:57199
dchengce2375e2016-01-12 01:09:07200By default, the script hides the output of the tool. The easiest way to change
201that is to `return 1` from the `main()` function of the clang tool.
andybons6eaa0c0d2015-08-26 20:12:52202
203## Testing
dchengce2375e2016-01-12 01:09:07204Synposis:
Daniel Cheng9ce2a302016-01-16 01:17:57205
andybons6eaa0c0d2015-08-26 20:12:52206```shell
Ramin Halavatieb3f807a2017-08-02 05:31:31207tools/clang/scripts/test_tool.py <tool name> [--apply-edits]
andybons3322f762015-08-24 21:37:09208```
andybons6eaa0c0d2015-08-26 20:12:52209
dchengce2375e2016-01-12 01:09:07210The name of the tool binary and the subdirectory for the tool in
211`//tools/clang` must match. The test runner finds all files that match the
Ramin Halavatieb3f807a2017-08-02 05:31:31212pattern `//tools/clang/<tool name>/tests/*-original.cc`, and runs the tool
213across those files.
214If `--apply-edits` switch is presented, tool outputs are applied to respective
215files and compared to the `*-expected.cc` version. If there is a mismatch, the
216result is saved in `*-actual.cc`.
217When `--apply-edits` switch is not presented, tool outputs are compared to
218`*-expected.txt` and if different, the result is saved in `*-actual.txt`. Note
219that in this case, only one test file is expected.
Daniel Cheng69413be2017-10-24 01:23:57220
221[//tools/clang]: https://chromium.googlesource.com/chromium/src/+/master/tools/clang/
222[clang-docs-match-finder]: http://clang.llvm.org/doxygen/classclang_1_1ast__matchers_1_1MatchFinder.html
223[clang-docs-match-callback]: http://clang.llvm.org/doxygen/classclang_1_1ast__matchers_1_1MatchFinder_1_1MatchCallback.html
224[matcher-reference]: http://clang.llvm.org/docs/LibASTMatchersReference.html
225[clang-docs-clang-tool-run]: http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1ClangTool.html#acec91f63b45ac7ee2d6c94cb9c10dab3
226[clang-docs-replacement]: http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1Replacement.html
227[clang-docs]: http://clang.llvm.org/doxygen/index.html
228[clang-tooling-tutorial]: http://clang.llvm.org/docs/LibASTMatchersTutorial.html
229[//tools/clang/blink_gc_plugin]: https://chromium.googlesource.com/chromium/src/+/master/tools/clang/blink_gc_plugin/
230[//tools/clang/plugins]: https://chromium.googlesource.com/chromium/src/+/master/tools/clang/plugins/
231[//tools/clang/rewrite_to_chrome_style]: https://chromium.googlesource.com/chromium/src/+/master/tools/clang/rewrite_to_chrome_style/
232[clang-tools-extra]: (https://github.com/llvm-mirror/clang-tools-extra)