blob: 6e22a1ba50fa534e6d42dc8e1772c971163730e5 [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>
14
andybons6eaa0c0d2015-08-26 20:12:5215## Caveats
andybons3322f762015-08-24 21:37:0916
dchengce2375e2016-01-12 01:09:0717An invocation of the clang tool runs on one build config. Code that only
18compiles on one platform or code that is guarded by a set of compile-time flags
19can be problematic. Performing a global refactoring typically requires running
20the tool once in each build config with code that needs to be updated.
21
22Other minor issues:
23
24* Requires a git checkout.
25* Requires [some hacks to run on Windows](https://codereview.chromium.org/718873004).
andybons6eaa0c0d2015-08-26 20:12:5226
27## Prerequisites
28
dchengce2375e2016-01-12 01:09:0729A Chromium checkout created with `fetch` should have everything needed.
andybons6eaa0c0d2015-08-26 20:12:5230
dchengce2375e2016-01-12 01:09:0731For convenience, add `third_party/llvm-build/Release+Asserts/bin` to `$PATH`.
andybons6eaa0c0d2015-08-26 20:12:5232
dchengce2375e2016-01-12 01:09:0733## Writing the tool
andybons6eaa0c0d2015-08-26 20:12:5234
dchengce2375e2016-01-12 01:09:0735LLVM uses C++11 and CMake. Source code for Chromium clang tools lives in
36[//tools/clang](https://chromium.googlesource.com/chromium/src/tools/clang/+/master).
37It is generally easiest to use one of the already-written tools as the base for
38writing a new tool.
andybons6eaa0c0d2015-08-26 20:12:5239
dchengce2375e2016-01-12 01:09:0740Chromium clang tools generally follow this pattern:
41
danakjef9f1fa2016-01-16 00:37:28421. Instantiate a [`clang::ast_matchers::MatchFinder`](http://clang.llvm.org/doxygen/classclang_1_1ast__matchers_1_1MatchFinder.html).
432. Call `addMatcher()` to register [`clang::ast_matchers::MatchFinder::MatchCallback`](http://clang.llvm.org/doxygen/classclang_1_1ast__matchers_1_1MatchFinder_1_1MatchCallback.html)
44 actions to execute when [matching](http://clang.llvm.org/docs/LibASTMatchersReference.html)
45 the AST.
dchengce2375e2016-01-12 01:09:07463. Create a new `clang::tooling::FrontendActionFactory` from the `MatchFinder`.
474. Run the action across the specified files with
48 [`clang::tooling::ClangTool::run`](http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1ClangTool.html#acec91f63b45ac7ee2d6c94cb9c10dab3).
danakjef9f1fa2016-01-16 00:37:28495. Serialize generated [`clang::tooling::Replacement`](http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1Replacement.html)s
50 to `stdout`.
dchengce2375e2016-01-12 01:09:0751
52Other useful references when writing the tool:
53
54* [Clang doxygen reference](http://clang.llvm.org/doxygen/index.html)
55* [Tutorial for building tools using LibTooling and LibASTMatchers](http://clang.llvm.org/docs/LibASTMatchersTutorial.html)
56
57### Edit serialization format
58```
59==== BEGIN EDITS ====
60r:::path/to/file1:::offset1:::length1:::replacement text
61r:::path/to/file2:::offset2:::length2:::replacement text
62
63 ...
64
65==== END EDITS ====
andybons3322f762015-08-24 21:37:0966```
67
dchengce2375e2016-01-12 01:09:0768The header and footer are required. Each line between the header and footer
69represents one edit. Fields are separated by `:::`, and the first field must
70be `r` (for replacement). In the future, this may be extended to handle header
71insertion/removal. A deletion is an edit with no replacement text.
andybons6eaa0c0d2015-08-26 20:12:5272
dchengce2375e2016-01-12 01:09:0773The edits are applied by [`run_tool.py`](#Running), which understands certain
74conventions:
75
76* The tool should munge newlines in replacement text to `\0`. The script
77 knows to translate `\0` back to newlines when applying edits.
78* When removing an element from a 'list' (e.g. function parameters,
79 initializers), the tool should emit a deletion for just the element. The
80 script understands how to extend the deletion to remove commas, etc. as
81 needed.
82
83TODO: Document more about `SourceLocation` and how spelling loc differs from
84expansion loc, etc.
85
86### Why not RefactoringTool?
danakjef9f1fa2016-01-16 00:37:2887While clang has a [`clang::tooling::RefactoringTool`](http://clang.llvm.org/doxygen/classclang_1_1tooling_1_1RefactoringTool.html)
88to automatically apply the generated replacements and save the results, it
89doesn't work well for Chromium:
dchengce2375e2016-01-12 01:09:0790
91* Clang tools run actions serially, so runtime scales poorly to tens of
92 thousands of files.
93* A parsing error in any file (quite common in NaCl source) prevents any of
94 the generated replacements from being applied.
95
96## Building
97Synopsis:
danakjef9f1fa2016-01-16 00:37:2898
andybons6eaa0c0d2015-08-26 20:12:5299```shell
danakj30d0f8c92016-01-28 00:26:33100tools/clang/scripts/update.py --bootstrap --force-local-build --without-android \
dchengce2375e2016-01-12 01:09:07101 --tools blink_gc_plugin plugins rewrite_to_chrome_style
102```
danakjef9f1fa2016-01-16 00:37:28103
dchengce2375e2016-01-12 01:09:07104Running this command builds the [Oilpan plugin](https://chromium.googlesource.com/chromium/src/+/master/tools/clang/blink_gc_plugin/),
105the [Chrome style
106plugin](https://chromium.googlesource.com/chromium/src/+/master/tools/clang/plugins/),
107and the [Blink to Chrome style rewriter](https://chromium.googlesource.com/chromium/src/+/master/tools/clang/rewrite_to_chrome_style/). Additional arguments to `--tools` should be the name of
108subdirectories in
109[//tools/clang](https://chromium.googlesource.com/chromium/src/+/master/tools/clang).
110Generally, `--tools` should always include `blink_gc_plugin` and `plugins`: otherwise, Chromium won't build.
111
danakj30d0f8c92016-01-28 00:26:33112It is important to use --bootstrap as there appear to be [bugs](https://crbug.com/580745)
113in the clang library this script produces if you build it with gcc, which is the default.
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
120ninja -C out/Debug
andybons3322f762015-08-24 21:37:09121```
andybons6eaa0c0d2015-08-26 20:12:52122
dchengce2375e2016-01-12 01:09:07123Then run the actual tool:
Daniel Cheng9ce2a302016-01-16 01:17:57124
125```shell
dchengce2375e2016-01-12 01:09:07126tools/clang/scripts/run_tool.py <toolname> \
127 --generate-compdb
128 out/Debug <path 1> <path 2> ...
129```
andybons6eaa0c0d2015-08-26 20:12:52130
dchengce2375e2016-01-12 01:09:07131`--generate-compdb` can be omitted if the compile DB was already generated and
132the list of build flags and source files has not changed since generation.
133
134`<path 1>`, `<path 2>`, etc are optional arguments to filter the files to run
135the tool across. This is helpful when sharding global refactorings into smaller
136chunks. For example, the following command will run the `empty_string` tool
137across just the files in `//base`:
138
139```shell
140tools/clang/scripts/run_tool.py empty_string \
141 --generated-compdb \
142 out/Debug base
143```
144
145## Debugging
146Dumping the AST for a file:
Daniel Cheng9ce2a302016-01-16 01:17:57147
andybons6eaa0c0d2015-08-26 20:12:52148```shell
andybons3322f762015-08-24 21:37:09149clang++ -cc1 -ast-dump foo.cc
150```
151
dchengce2375e2016-01-12 01:09:07152Using `clang-query` to dynamically test matchers (requires checking out
153and building [clang-tools-extras](https://github.com/llvm-mirror/clang-tools-extra)):
Daniel Cheng9ce2a302016-01-16 01:17:57154
andybons6eaa0c0d2015-08-26 20:12:52155```shell
dchengce2375e2016-01-12 01:09:07156clang-query -p path/to/compdb base/memory/ref_counted.cc
andybons3322f762015-08-24 21:37:09157```
158
dchengce2375e2016-01-12 01:09:07159`printf` debugging:
Daniel Cheng9ce2a302016-01-16 01:17:57160
dchengce2375e2016-01-12 01:09:07161```c++
162 clang::Decl* decl = result.Nodes.getNodeAs<clang::Decl>("decl");
163 decl->dumpColor();
164 clang::Stmt* stmt = result.Nodes.getNodeAs<clang::Stmt>("stmt");
165 stmt->dumpColor();
andybons3322f762015-08-24 21:37:09166```
Daniel Cheng9ce2a302016-01-16 01:17:57167
dchengce2375e2016-01-12 01:09:07168By default, the script hides the output of the tool. The easiest way to change
169that is to `return 1` from the `main()` function of the clang tool.
andybons6eaa0c0d2015-08-26 20:12:52170
171## Testing
dchengce2375e2016-01-12 01:09:07172Synposis:
Daniel Cheng9ce2a302016-01-16 01:17:57173
andybons6eaa0c0d2015-08-26 20:12:52174```shell
lukasza1333f1b92016-08-27 00:24:43175tools/clang/scripts/test_tool.py <tool name>
andybons3322f762015-08-24 21:37:09176```
andybons6eaa0c0d2015-08-26 20:12:52177
dchengce2375e2016-01-12 01:09:07178The name of the tool binary and the subdirectory for the tool in
179`//tools/clang` must match. The test runner finds all files that match the
180pattern `//tools/clang/<tool name>/tests/*-original.cc`, runs the tool across
181those files, and compared it to the `*-expected.cc` version. If there is a
182mismatch, the result is saved in `*-actual.cc`.