OLD | NEW |
1 # Clang Tool Refactoring | 1 # Clang Tool Refactoring |
2 | 2 |
3 [TOC] | 3 [TOC] |
4 | 4 |
5 ## Introduction | 5 ## Introduction |
6 | 6 |
7 Clang tools can help with global refactorings of Chromium code. Clang tools can | 7 Clang tools can help with global refactorings of Chromium code. Clang tools can |
8 take advantage of clang's AST to perform refactorings that would be impossible | 8 take advantage of clang's AST to perform refactorings that would be impossible |
9 with a traditional find-and-replace regexp: | 9 with a traditional find-and-replace regexp: |
10 | 10 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
62 ... | 62 ... |
63 | 63 |
64 ==== END EDITS ==== | 64 ==== END EDITS ==== |
65 ``` | 65 ``` |
66 | 66 |
67 The header and footer are required. Each line between the header and footer | 67 The header and footer are required. Each line between the header and footer |
68 represents one edit. Fields are separated by `:::`, and the first field must | 68 represents one edit. Fields are separated by `:::`, and the first field must |
69 be `r` (for replacement). In the future, this may be extended to handle header | 69 be `r` (for replacement). In the future, this may be extended to handle header |
70 insertion/removal. A deletion is an edit with no replacement text. | 70 insertion/removal. A deletion is an edit with no replacement text. |
71 | 71 |
72 The edits are applied by [`run_tool.py`](#Running), which understands certain | 72 The edits are applied by [`apply_edits.py`](#Running), which understands certain |
73 conventions: | 73 conventions: |
74 | 74 |
75 * The tool should munge newlines in replacement text to `\0`. The script | 75 * The clang tool should munge newlines in replacement text to `\0`. The script |
76 knows to translate `\0` back to newlines when applying edits. | 76 knows to translate `\0` back to newlines when applying edits. |
77 * When removing an element from a 'list' (e.g. function parameters, | 77 * When removing an element from a 'list' (e.g. function parameters, |
78 initializers), the tool should emit a deletion for just the element. The | 78 initializers), the clang tool should emit a deletion for just the element. |
79 script understands how to extend the deletion to remove commas, etc. as | 79 The script understands how to extend the deletion to remove commas, etc. as |
80 needed. | 80 needed. |
81 | 81 |
82 TODO: Document more about `SourceLocation` and how spelling loc differs from | 82 TODO: Document more about `SourceLocation` and how spelling loc differs from |
83 expansion loc, etc. | 83 expansion loc, etc. |
84 | 84 |
85 ### Why not RefactoringTool? | 85 ### Why not RefactoringTool? |
86 While clang has a [`clang::tooling::RefactoringTool`](http://clang.llvm.org/doxy
gen/classclang_1_1tooling_1_1RefactoringTool.html) | 86 While clang has a [`clang::tooling::RefactoringTool`](http://clang.llvm.org/doxy
gen/classclang_1_1tooling_1_1RefactoringTool.html) |
87 to automatically apply the generated replacements and save the results, it | 87 to automatically apply the generated replacements and save the results, it |
88 doesn't work well for Chromium: | 88 doesn't work well for Chromium: |
89 | 89 |
(...skipping 21 matching lines...) Expand all Loading... |
111 It is important to use --bootstrap as there appear to be [bugs](https://crbug.co
m/580745) | 111 It is important to use --bootstrap as there appear to be [bugs](https://crbug.co
m/580745) |
112 in the clang library this script produces if you build it with gcc, which is the
default. | 112 in the clang library this script produces if you build it with gcc, which is the
default. |
113 | 113 |
114 ## Running | 114 ## Running |
115 First, build all Chromium targets to avoid failures due to missing dependencies | 115 First, build all Chromium targets to avoid failures due to missing dependencies |
116 that are generated as part of the build: | 116 that are generated as part of the build: |
117 | 117 |
118 ```shell | 118 ```shell |
119 ninja -C out/Debug # For non-Windows | 119 ninja -C out/Debug # For non-Windows |
120 ninja -d keeprsp -C out/Debug # For Windows | 120 ninja -d keeprsp -C out/Debug # For Windows |
| 121 |
| 122 # experimental alternative: |
| 123 $gen_targets = $(ninja -C out/gn -t targets all \ |
| 124 | grep '^gen/[^: ]*\.[ch][pc]*:' \ |
| 125 | cut -f 1 -d :`) |
| 126 ninja -C out/Debug $gen_targets |
121 ``` | 127 ``` |
122 | 128 |
123 On Windows, generate the compile DB first, and after making any source changes. | 129 On Windows, generate the compile DB first, and after making any source changes. |
124 Then omit the `--generate-compdb` in later steps. | 130 Then omit the `--generate-compdb` in later steps. |
125 | 131 |
126 ```shell | 132 ```shell |
127 tools/clang/scripts/generate_win_compdb.py out/Debug | 133 tools/clang/scripts/generate_win_compdb.py out/Debug |
128 ``` | 134 ``` |
129 | 135 |
130 Then run the actual tool: | 136 Then run the actual clang tool to generate a list of edits: |
131 | 137 |
132 ```shell | 138 ```shell |
133 tools/clang/scripts/run_tool.py <toolname> \ | 139 tools/clang/scripts/run_tool.py <toolname> \ |
134 --generate-compdb | 140 --generate-compdb |
135 out/Debug <path 1> <path 2> ... | 141 out/Debug <path 1> <path 2> ... >/tmp/list-of-edits.debug |
136 ``` | 142 ``` |
137 | 143 |
138 `--generate-compdb` can be omitted if the compile DB was already generated and | 144 `--generate-compdb` can be omitted if the compile DB was already generated and |
139 the list of build flags and source files has not changed since generation. | 145 the list of build flags and source files has not changed since generation. |
140 | 146 |
141 `<path 1>`, `<path 2>`, etc are optional arguments to filter the files to run | 147 `<path 1>`, `<path 2>`, etc are optional arguments to filter the files to run |
142 the tool across. This is helpful when sharding global refactorings into smaller | 148 the tool against. This is helpful when sharding global refactorings into smaller |
143 chunks. For example, the following command will run the `empty_string` tool | 149 chunks. For example, the following command will run the `empty_string` tool |
144 across just the files in `//base`: | 150 against just the `.c`, `.cc`, `.cpp`, `.m`, `.mm` files in `//net`. Note that |
| 151 the filtering is not applied to the *output* of the tool - the tool can emit |
| 152 edits that apply to files outside of `//cc` (i.e. edits that apply to headers |
| 153 from `//base` that got included by source files in `//cc`). |
145 | 154 |
146 ```shell | 155 ```shell |
147 tools/clang/scripts/run_tool.py empty_string \ | 156 tools/clang/scripts/run_tool.py empty_string \ |
148 --generated-compdb \ | 157 --generated-compdb \ |
149 out/Debug base | 158 out/Debug net >/tmp/list-of-edits.debug |
150 ``` | 159 ``` |
151 | 160 |
| 161 Note that some header files might only be included from generated files (e.g. |
| 162 from only from some `.cpp` files under out/Debug/gen). To make sure that |
| 163 contents of such header files are processed by the clang tool, the clang tool |
| 164 needs to be run against the generated files. The only way to accomplish this |
| 165 today is to pass `--all` switch to `run_tool.py` - this will run the clang tool |
| 166 against all the sources from the compilation database. |
| 167 |
| 168 Finally, apply the edits as follows: |
| 169 |
| 170 ```shell |
| 171 cat /tmp/list-of-edits.debug \ |
| 172 | tools/clang/scripts/extract_edits.py \ |
| 173 | tools/clang/scripts/apply_edits.py out/Debug <path 1> <path 2> ... |
| 174 ``` |
| 175 |
| 176 The apply_edits.py tool will only apply edits to files actually under control of |
| 177 `git`. `<path 1>`, `<path 2>`, etc are optional arguments to further filter the |
| 178 files that the edits are applied to. Note that semantics of these filters is |
| 179 distinctly different from the arguments of `run_tool.py` filters - one set of |
| 180 filters controls which files are edited, the other set of filters controls which |
| 181 files the clang tool is run against. |
| 182 |
152 ## Debugging | 183 ## Debugging |
153 Dumping the AST for a file: | 184 Dumping the AST for a file: |
154 | 185 |
155 ```shell | 186 ```shell |
156 clang++ -cc1 -ast-dump foo.cc | 187 clang++ -cc1 -ast-dump foo.cc |
157 ``` | 188 ``` |
158 | 189 |
159 Using `clang-query` to dynamically test matchers (requires checking out | 190 Using `clang-query` to dynamically test matchers (requires checking out |
160 and building [clang-tools-extras](https://github.com/llvm-mirror/clang-tools-ext
ra)): | 191 and building [clang-tools-extras](https://github.com/llvm-mirror/clang-tools-ext
ra)): |
161 | 192 |
(...skipping 18 matching lines...) Expand all Loading... |
180 | 211 |
181 ```shell | 212 ```shell |
182 tools/clang/scripts/test_tool.py <tool name> | 213 tools/clang/scripts/test_tool.py <tool name> |
183 ``` | 214 ``` |
184 | 215 |
185 The name of the tool binary and the subdirectory for the tool in | 216 The name of the tool binary and the subdirectory for the tool in |
186 `//tools/clang` must match. The test runner finds all files that match the | 217 `//tools/clang` must match. The test runner finds all files that match the |
187 pattern `//tools/clang/<tool name>/tests/*-original.cc`, runs the tool across | 218 pattern `//tools/clang/<tool name>/tests/*-original.cc`, runs the tool across |
188 those files, and compared it to the `*-expected.cc` version. If there is a | 219 those files, and compared it to the `*-expected.cc` version. If there is a |
189 mismatch, the result is saved in `*-actual.cc`. | 220 mismatch, the result is saved in `*-actual.cc`. |
OLD | NEW |