annotate clang-tools-extra/clang-tidy/utils/IncludeSorter.cpp @ 204:e348f3e5c8b2

ReadFromString worked.
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Sat, 05 Jun 2021 15:35:13 +0900
parents 0572611fdcc8
children 2e18cbf3894f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===---------- IncludeSorter.cpp - clang-tidy ----------------------------===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 #include "IncludeSorter.h"
anatofuz
parents:
diff changeset
10 #include "clang/Lex/Lexer.h"
anatofuz
parents:
diff changeset
11
anatofuz
parents:
diff changeset
12 namespace clang {
anatofuz
parents:
diff changeset
13 namespace tidy {
anatofuz
parents:
diff changeset
14 namespace utils {
anatofuz
parents:
diff changeset
15
anatofuz
parents:
diff changeset
16 namespace {
anatofuz
parents:
diff changeset
17
anatofuz
parents:
diff changeset
18 StringRef RemoveFirstSuffix(StringRef Str, ArrayRef<const char *> Suffixes) {
anatofuz
parents:
diff changeset
19 for (StringRef Suffix : Suffixes) {
anatofuz
parents:
diff changeset
20 if (Str.endswith(Suffix)) {
anatofuz
parents:
diff changeset
21 return Str.substr(0, Str.size() - Suffix.size());
anatofuz
parents:
diff changeset
22 }
anatofuz
parents:
diff changeset
23 }
anatofuz
parents:
diff changeset
24 return Str;
anatofuz
parents:
diff changeset
25 }
anatofuz
parents:
diff changeset
26
anatofuz
parents:
diff changeset
27 StringRef MakeCanonicalName(StringRef Str, IncludeSorter::IncludeStyle Style) {
anatofuz
parents:
diff changeset
28 // The list of suffixes to remove from source file names to get the
anatofuz
parents:
diff changeset
29 // "canonical" file names.
anatofuz
parents:
diff changeset
30 // E.g. tools/sort_includes.cc and tools/sort_includes_test.cc
anatofuz
parents:
diff changeset
31 // would both canonicalize to tools/sort_includes and tools/sort_includes.h
anatofuz
parents:
diff changeset
32 // (once canonicalized) will match as being the main include file associated
anatofuz
parents:
diff changeset
33 // with the source files.
anatofuz
parents:
diff changeset
34 if (Style == IncludeSorter::IS_LLVM) {
anatofuz
parents:
diff changeset
35 return RemoveFirstSuffix(
anatofuz
parents:
diff changeset
36 RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}), {"Test"});
anatofuz
parents:
diff changeset
37 }
anatofuz
parents:
diff changeset
38 return RemoveFirstSuffix(
anatofuz
parents:
diff changeset
39 RemoveFirstSuffix(Str, {".cc", ".cpp", ".c", ".h", ".hpp"}),
anatofuz
parents:
diff changeset
40 {"_unittest", "_regtest", "_test"});
anatofuz
parents:
diff changeset
41 }
anatofuz
parents:
diff changeset
42
anatofuz
parents:
diff changeset
43 // Scan to the end of the line and return the offset of the next line.
anatofuz
parents:
diff changeset
44 size_t FindNextLine(const char *Text) {
anatofuz
parents:
diff changeset
45 size_t EOLIndex = std::strcspn(Text, "\n");
anatofuz
parents:
diff changeset
46 return Text[EOLIndex] == '\0' ? EOLIndex : EOLIndex + 1;
anatofuz
parents:
diff changeset
47 }
anatofuz
parents:
diff changeset
48
anatofuz
parents:
diff changeset
49 IncludeSorter::IncludeKinds
anatofuz
parents:
diff changeset
50 DetermineIncludeKind(StringRef CanonicalFile, StringRef IncludeFile,
anatofuz
parents:
diff changeset
51 bool IsAngled, IncludeSorter::IncludeStyle Style) {
anatofuz
parents:
diff changeset
52 // Compute the two "canonical" forms of the include's filename sans extension.
anatofuz
parents:
diff changeset
53 // The first form is the include's filename without ".h" or "-inl.h" at the
anatofuz
parents:
diff changeset
54 // end. The second form is the first form with "/public/" in the file path
anatofuz
parents:
diff changeset
55 // replaced by "/internal/".
anatofuz
parents:
diff changeset
56 if (IsAngled) {
anatofuz
parents:
diff changeset
57 // If the system include (<foo>) ends with ".h", then it is a normal C-style
anatofuz
parents:
diff changeset
58 // include. Otherwise assume it is a C++-style extensionless include.
anatofuz
parents:
diff changeset
59 return IncludeFile.endswith(".h") ? IncludeSorter::IK_CSystemInclude
anatofuz
parents:
diff changeset
60 : IncludeSorter::IK_CXXSystemInclude;
anatofuz
parents:
diff changeset
61 }
anatofuz
parents:
diff changeset
62 StringRef CanonicalInclude = MakeCanonicalName(IncludeFile, Style);
anatofuz
parents:
diff changeset
63 if (CanonicalFile.endswith(CanonicalInclude)
anatofuz
parents:
diff changeset
64 || CanonicalInclude.endswith(CanonicalFile)) {
anatofuz
parents:
diff changeset
65 return IncludeSorter::IK_MainTUInclude;
anatofuz
parents:
diff changeset
66 }
anatofuz
parents:
diff changeset
67 if (Style == IncludeSorter::IS_Google) {
anatofuz
parents:
diff changeset
68 std::pair<StringRef, StringRef> Parts = CanonicalInclude.split("/public/");
anatofuz
parents:
diff changeset
69 std::string AltCanonicalInclude =
anatofuz
parents:
diff changeset
70 Parts.first.str() + "/internal/" + Parts.second.str();
anatofuz
parents:
diff changeset
71 std::string ProtoCanonicalInclude =
anatofuz
parents:
diff changeset
72 Parts.first.str() + "/proto/" + Parts.second.str();
anatofuz
parents:
diff changeset
73
anatofuz
parents:
diff changeset
74 // Determine the kind of this inclusion.
anatofuz
parents:
diff changeset
75 if (CanonicalFile.equals(AltCanonicalInclude) ||
anatofuz
parents:
diff changeset
76 CanonicalFile.equals(ProtoCanonicalInclude)) {
anatofuz
parents:
diff changeset
77 return IncludeSorter::IK_MainTUInclude;
anatofuz
parents:
diff changeset
78 }
anatofuz
parents:
diff changeset
79 }
anatofuz
parents:
diff changeset
80 return IncludeSorter::IK_NonSystemInclude;
anatofuz
parents:
diff changeset
81 }
anatofuz
parents:
diff changeset
82
anatofuz
parents:
diff changeset
83 } // namespace
anatofuz
parents:
diff changeset
84
anatofuz
parents:
diff changeset
85 IncludeSorter::IncludeSorter(const SourceManager *SourceMgr,
anatofuz
parents:
diff changeset
86 const LangOptions *LangOpts, const FileID FileID,
anatofuz
parents:
diff changeset
87 StringRef FileName, IncludeStyle Style)
anatofuz
parents:
diff changeset
88 : SourceMgr(SourceMgr), LangOpts(LangOpts), Style(Style),
anatofuz
parents:
diff changeset
89 CurrentFileID(FileID), CanonicalFile(MakeCanonicalName(FileName, Style)) {
anatofuz
parents:
diff changeset
90 }
anatofuz
parents:
diff changeset
91
anatofuz
parents:
diff changeset
92 void IncludeSorter::AddInclude(StringRef FileName, bool IsAngled,
anatofuz
parents:
diff changeset
93 SourceLocation HashLocation,
anatofuz
parents:
diff changeset
94 SourceLocation EndLocation) {
anatofuz
parents:
diff changeset
95 int Offset = FindNextLine(SourceMgr->getCharacterData(EndLocation));
anatofuz
parents:
diff changeset
96
anatofuz
parents:
diff changeset
97 // Record the relevant location information for this inclusion directive.
anatofuz
parents:
diff changeset
98 IncludeLocations[FileName].push_back(
anatofuz
parents:
diff changeset
99 SourceRange(HashLocation, EndLocation.getLocWithOffset(Offset)));
anatofuz
parents:
diff changeset
100 SourceLocations.push_back(IncludeLocations[FileName].back());
anatofuz
parents:
diff changeset
101
anatofuz
parents:
diff changeset
102 // Stop if this inclusion is a duplicate.
anatofuz
parents:
diff changeset
103 if (IncludeLocations[FileName].size() > 1)
anatofuz
parents:
diff changeset
104 return;
anatofuz
parents:
diff changeset
105
anatofuz
parents:
diff changeset
106 // Add the included file's name to the appropriate bucket.
anatofuz
parents:
diff changeset
107 IncludeKinds Kind =
anatofuz
parents:
diff changeset
108 DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
anatofuz
parents:
diff changeset
109 if (Kind != IK_InvalidInclude)
anatofuz
parents:
diff changeset
110 IncludeBucket[Kind].push_back(FileName.str());
anatofuz
parents:
diff changeset
111 }
anatofuz
parents:
diff changeset
112
anatofuz
parents:
diff changeset
113 Optional<FixItHint> IncludeSorter::CreateIncludeInsertion(StringRef FileName,
anatofuz
parents:
diff changeset
114 bool IsAngled) {
anatofuz
parents:
diff changeset
115 std::string IncludeStmt =
anatofuz
parents:
diff changeset
116 IsAngled ? llvm::Twine("#include <" + FileName + ">\n").str()
anatofuz
parents:
diff changeset
117 : llvm::Twine("#include \"" + FileName + "\"\n").str();
anatofuz
parents:
diff changeset
118 if (SourceLocations.empty()) {
anatofuz
parents:
diff changeset
119 // If there are no includes in this file, add it in the first line.
anatofuz
parents:
diff changeset
120 // FIXME: insert after the file comment or the header guard, if present.
anatofuz
parents:
diff changeset
121 IncludeStmt.append("\n");
anatofuz
parents:
diff changeset
122 return FixItHint::CreateInsertion(
anatofuz
parents:
diff changeset
123 SourceMgr->getLocForStartOfFile(CurrentFileID), IncludeStmt);
anatofuz
parents:
diff changeset
124 }
anatofuz
parents:
diff changeset
125
anatofuz
parents:
diff changeset
126 auto IncludeKind =
anatofuz
parents:
diff changeset
127 DetermineIncludeKind(CanonicalFile, FileName, IsAngled, Style);
anatofuz
parents:
diff changeset
128
anatofuz
parents:
diff changeset
129 if (!IncludeBucket[IncludeKind].empty()) {
anatofuz
parents:
diff changeset
130 for (const std::string &IncludeEntry : IncludeBucket[IncludeKind]) {
anatofuz
parents:
diff changeset
131 if (FileName < IncludeEntry) {
anatofuz
parents:
diff changeset
132 const auto &Location = IncludeLocations[IncludeEntry][0];
anatofuz
parents:
diff changeset
133 return FixItHint::CreateInsertion(Location.getBegin(), IncludeStmt);
anatofuz
parents:
diff changeset
134 } else if (FileName == IncludeEntry) {
anatofuz
parents:
diff changeset
135 return llvm::None;
anatofuz
parents:
diff changeset
136 }
anatofuz
parents:
diff changeset
137 }
anatofuz
parents:
diff changeset
138 // FileName comes after all include entries in bucket, insert it after
anatofuz
parents:
diff changeset
139 // last.
anatofuz
parents:
diff changeset
140 const std::string &LastInclude = IncludeBucket[IncludeKind].back();
anatofuz
parents:
diff changeset
141 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
anatofuz
parents:
diff changeset
142 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
anatofuz
parents:
diff changeset
143 IncludeStmt);
anatofuz
parents:
diff changeset
144 }
anatofuz
parents:
diff changeset
145 // Find the non-empty include bucket to be sorted directly above
anatofuz
parents:
diff changeset
146 // 'IncludeKind'. If such a bucket exists, we'll want to sort the include
anatofuz
parents:
diff changeset
147 // after that bucket. If no such bucket exists, find the first non-empty
anatofuz
parents:
diff changeset
148 // include bucket in the file. In that case, we'll want to sort the include
anatofuz
parents:
diff changeset
149 // before that bucket.
anatofuz
parents:
diff changeset
150 IncludeKinds NonEmptyKind = IK_InvalidInclude;
anatofuz
parents:
diff changeset
151 for (int i = IK_InvalidInclude - 1; i >= 0; --i) {
anatofuz
parents:
diff changeset
152 if (!IncludeBucket[i].empty()) {
anatofuz
parents:
diff changeset
153 NonEmptyKind = static_cast<IncludeKinds>(i);
anatofuz
parents:
diff changeset
154 if (NonEmptyKind < IncludeKind)
anatofuz
parents:
diff changeset
155 break;
anatofuz
parents:
diff changeset
156 }
anatofuz
parents:
diff changeset
157 }
anatofuz
parents:
diff changeset
158 if (NonEmptyKind == IK_InvalidInclude) {
anatofuz
parents:
diff changeset
159 return llvm::None;
anatofuz
parents:
diff changeset
160 }
anatofuz
parents:
diff changeset
161
anatofuz
parents:
diff changeset
162 if (NonEmptyKind < IncludeKind) {
anatofuz
parents:
diff changeset
163 // Create a block after.
anatofuz
parents:
diff changeset
164 const std::string &LastInclude = IncludeBucket[NonEmptyKind].back();
anatofuz
parents:
diff changeset
165 SourceRange LastIncludeLocation = IncludeLocations[LastInclude].back();
anatofuz
parents:
diff changeset
166 IncludeStmt = '\n' + IncludeStmt;
anatofuz
parents:
diff changeset
167 return FixItHint::CreateInsertion(LastIncludeLocation.getEnd(),
anatofuz
parents:
diff changeset
168 IncludeStmt);
anatofuz
parents:
diff changeset
169 }
anatofuz
parents:
diff changeset
170 // Create a block before.
anatofuz
parents:
diff changeset
171 const std::string &FirstInclude = IncludeBucket[NonEmptyKind][0];
anatofuz
parents:
diff changeset
172 SourceRange FirstIncludeLocation = IncludeLocations[FirstInclude].back();
anatofuz
parents:
diff changeset
173 IncludeStmt.append("\n");
anatofuz
parents:
diff changeset
174 return FixItHint::CreateInsertion(FirstIncludeLocation.getBegin(),
anatofuz
parents:
diff changeset
175 IncludeStmt);
anatofuz
parents:
diff changeset
176 }
anatofuz
parents:
diff changeset
177
anatofuz
parents:
diff changeset
178 std::vector<FixItHint> IncludeSorter::GetEdits() {
anatofuz
parents:
diff changeset
179 if (SourceLocations.empty())
anatofuz
parents:
diff changeset
180 return {};
anatofuz
parents:
diff changeset
181
anatofuz
parents:
diff changeset
182 typedef std::map<int, std::pair<SourceRange, std::string>>
anatofuz
parents:
diff changeset
183 FileLineToSourceEditMap;
anatofuz
parents:
diff changeset
184 FileLineToSourceEditMap Edits;
anatofuz
parents:
diff changeset
185 auto SourceLocationIterator = SourceLocations.begin();
anatofuz
parents:
diff changeset
186 auto SourceLocationIteratorEnd = SourceLocations.end();
anatofuz
parents:
diff changeset
187
anatofuz
parents:
diff changeset
188 // Compute the Edits that need to be done to each line to add, replace, or
anatofuz
parents:
diff changeset
189 // delete inclusions.
anatofuz
parents:
diff changeset
190 for (int IncludeKind = 0; IncludeKind < IK_InvalidInclude; ++IncludeKind) {
anatofuz
parents:
diff changeset
191 std::sort(IncludeBucket[IncludeKind].begin(),
anatofuz
parents:
diff changeset
192 IncludeBucket[IncludeKind].end());
anatofuz
parents:
diff changeset
193 for (const auto &IncludeEntry : IncludeBucket[IncludeKind]) {
anatofuz
parents:
diff changeset
194 auto &Location = IncludeLocations[IncludeEntry];
anatofuz
parents:
diff changeset
195 SourceRangeVector::iterator LocationIterator = Location.begin();
anatofuz
parents:
diff changeset
196 SourceRangeVector::iterator LocationIteratorEnd = Location.end();
anatofuz
parents:
diff changeset
197 SourceRange FirstLocation = *LocationIterator;
anatofuz
parents:
diff changeset
198
anatofuz
parents:
diff changeset
199 // If the first occurrence of a particular include is on the current
anatofuz
parents:
diff changeset
200 // source line we are examining, leave it alone.
anatofuz
parents:
diff changeset
201 if (FirstLocation == *SourceLocationIterator)
anatofuz
parents:
diff changeset
202 ++LocationIterator;
anatofuz
parents:
diff changeset
203
anatofuz
parents:
diff changeset
204 // Add the deletion Edits for any (remaining) instances of this inclusion,
anatofuz
parents:
diff changeset
205 // and remove their Locations from the source Locations to be processed.
anatofuz
parents:
diff changeset
206 for (; LocationIterator != LocationIteratorEnd; ++LocationIterator) {
anatofuz
parents:
diff changeset
207 int LineNumber =
anatofuz
parents:
diff changeset
208 SourceMgr->getSpellingLineNumber(LocationIterator->getBegin());
anatofuz
parents:
diff changeset
209 Edits[LineNumber] = std::make_pair(*LocationIterator, "");
anatofuz
parents:
diff changeset
210 SourceLocationIteratorEnd =
anatofuz
parents:
diff changeset
211 std::remove(SourceLocationIterator, SourceLocationIteratorEnd,
anatofuz
parents:
diff changeset
212 *LocationIterator);
anatofuz
parents:
diff changeset
213 }
anatofuz
parents:
diff changeset
214
anatofuz
parents:
diff changeset
215 if (FirstLocation == *SourceLocationIterator) {
anatofuz
parents:
diff changeset
216 // Do nothing except move to the next source Location (Location of an
anatofuz
parents:
diff changeset
217 // inclusion in the original, unchanged source file).
anatofuz
parents:
diff changeset
218 ++SourceLocationIterator;
anatofuz
parents:
diff changeset
219 continue;
anatofuz
parents:
diff changeset
220 }
anatofuz
parents:
diff changeset
221
anatofuz
parents:
diff changeset
222 // Add (or append to) the replacement text for this line in source file.
anatofuz
parents:
diff changeset
223 int LineNumber =
anatofuz
parents:
diff changeset
224 SourceMgr->getSpellingLineNumber(SourceLocationIterator->getBegin());
anatofuz
parents:
diff changeset
225 if (Edits.find(LineNumber) == Edits.end()) {
anatofuz
parents:
diff changeset
226 Edits[LineNumber].first =
anatofuz
parents:
diff changeset
227 SourceRange(SourceLocationIterator->getBegin());
anatofuz
parents:
diff changeset
228 }
anatofuz
parents:
diff changeset
229 StringRef SourceText = Lexer::getSourceText(
anatofuz
parents:
diff changeset
230 CharSourceRange::getCharRange(FirstLocation), *SourceMgr, *LangOpts);
anatofuz
parents:
diff changeset
231 Edits[LineNumber].second.append(SourceText.data(), SourceText.size());
anatofuz
parents:
diff changeset
232 }
anatofuz
parents:
diff changeset
233
anatofuz
parents:
diff changeset
234 // Clear the bucket.
anatofuz
parents:
diff changeset
235 IncludeBucket[IncludeKind].clear();
anatofuz
parents:
diff changeset
236 }
anatofuz
parents:
diff changeset
237
anatofuz
parents:
diff changeset
238 // Go through the single-line Edits and combine them into blocks of Edits.
anatofuz
parents:
diff changeset
239 int CurrentEndLine = 0;
anatofuz
parents:
diff changeset
240 SourceRange CurrentRange;
anatofuz
parents:
diff changeset
241 std::string CurrentText;
anatofuz
parents:
diff changeset
242 std::vector<FixItHint> Fixes;
anatofuz
parents:
diff changeset
243 for (const auto &LineEdit : Edits) {
anatofuz
parents:
diff changeset
244 // If the current edit is on the next line after the previous edit, add it
anatofuz
parents:
diff changeset
245 // to the current block edit.
anatofuz
parents:
diff changeset
246 if (LineEdit.first == CurrentEndLine + 1 &&
anatofuz
parents:
diff changeset
247 CurrentRange.getBegin() != CurrentRange.getEnd()) {
anatofuz
parents:
diff changeset
248 SourceRange EditRange = LineEdit.second.first;
anatofuz
parents:
diff changeset
249 if (EditRange.getBegin() != EditRange.getEnd()) {
anatofuz
parents:
diff changeset
250 ++CurrentEndLine;
anatofuz
parents:
diff changeset
251 CurrentRange.setEnd(EditRange.getEnd());
anatofuz
parents:
diff changeset
252 }
anatofuz
parents:
diff changeset
253 CurrentText += LineEdit.second.second;
anatofuz
parents:
diff changeset
254 // Otherwise report the current block edit and start a new block.
anatofuz
parents:
diff changeset
255 } else {
anatofuz
parents:
diff changeset
256 if (CurrentEndLine) {
anatofuz
parents:
diff changeset
257 Fixes.push_back(FixItHint::CreateReplacement(
anatofuz
parents:
diff changeset
258 CharSourceRange::getCharRange(CurrentRange), CurrentText));
anatofuz
parents:
diff changeset
259 }
anatofuz
parents:
diff changeset
260
anatofuz
parents:
diff changeset
261 CurrentEndLine = LineEdit.first;
anatofuz
parents:
diff changeset
262 CurrentRange = LineEdit.second.first;
anatofuz
parents:
diff changeset
263 CurrentText = LineEdit.second.second;
anatofuz
parents:
diff changeset
264 }
anatofuz
parents:
diff changeset
265 }
anatofuz
parents:
diff changeset
266 // Finally, report the current block edit if there is one.
anatofuz
parents:
diff changeset
267 if (CurrentEndLine) {
anatofuz
parents:
diff changeset
268 Fixes.push_back(FixItHint::CreateReplacement(
anatofuz
parents:
diff changeset
269 CharSourceRange::getCharRange(CurrentRange), CurrentText));
anatofuz
parents:
diff changeset
270 }
anatofuz
parents:
diff changeset
271
anatofuz
parents:
diff changeset
272 // Reset the remaining internal state.
anatofuz
parents:
diff changeset
273 SourceLocations.clear();
anatofuz
parents:
diff changeset
274 IncludeLocations.clear();
anatofuz
parents:
diff changeset
275 return Fixes;
anatofuz
parents:
diff changeset
276 }
anatofuz
parents:
diff changeset
277
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
278 llvm::ArrayRef<std::pair<StringRef, IncludeSorter::IncludeStyle>>
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
279 IncludeSorter::getMapping() {
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
280 static constexpr std::pair<StringRef, IncludeSorter::IncludeStyle> Mapping[] =
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
281 {{"llvm", IS_LLVM}, {"google", IS_Google}};
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
282 return makeArrayRef(Mapping);
150
anatofuz
parents:
diff changeset
283 }
anatofuz
parents:
diff changeset
284
anatofuz
parents:
diff changeset
285 } // namespace utils
anatofuz
parents:
diff changeset
286 } // namespace tidy
anatofuz
parents:
diff changeset
287 } // namespace clang