1 | /**************************************************************************** |
2 | * Copyright (C) 2012-2016 Woboq GmbH |
3 | * Olivier Goffart <contact at woboq.com> |
4 | * https://woboq.com/codebrowser.html |
5 | * |
6 | * This file is part of the Woboq Code Browser. |
7 | * |
8 | * Commercial License Usage: |
9 | * Licensees holding valid commercial licenses provided by Woboq may use |
10 | * this file in accordance with the terms contained in a written agreement |
11 | * between the licensee and Woboq. |
12 | * For further information see https://woboq.com/codebrowser.html |
13 | * |
14 | * Alternatively, this work may be used under a Creative Commons |
15 | * Attribution-NonCommercial-ShareAlike 3.0 (CC-BY-NC-SA 3.0) License. |
16 | * http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US |
17 | * This license does not allow you to use the code browser to assist the |
18 | * development of your commercial software. If you intent to do so, consider |
19 | * purchasing a commercial licence. |
20 | ****************************************************************************/ |
21 | |
22 | #include "annotator.h" |
23 | #include "generator.h" |
24 | #include "filesystem.h" |
25 | #include <clang/Basic/SourceManager.h> |
26 | #include <clang/Basic/FileManager.h> |
27 | #include <clang/Basic/Version.h> |
28 | #include <clang/AST/DeclBase.h> |
29 | #include <clang/AST/Decl.h> |
30 | #include <clang/AST/Mangle.h> |
31 | #include <clang/AST/DeclCXX.h> |
32 | #include <clang/AST/DeclTemplate.h> |
33 | #include <clang/AST/PrettyPrinter.h> |
34 | #include <clang/AST/ASTContext.h> |
35 | #include <clang/AST/RecordLayout.h> |
36 | #include <clang/Lex/Lexer.h> |
37 | #include <clang/Lex/Preprocessor.h> |
38 | #include <clang/Sema/Sema.h> |
39 | #include <clang/Tooling/Tooling.h> |
40 | |
41 | #include <iostream> |
42 | #include <sstream> |
43 | #include <fstream> |
44 | #include <time.h> |
45 | |
46 | #include <llvm/Support/raw_ostream.h> |
47 | #include <llvm/ADT/SmallString.h> |
48 | #include <llvm/Support/FileSystem.h> |
49 | #include <llvm/Support/Path.h> |
50 | |
51 | #include "stringbuilder.h" |
52 | #include "projectmanager.h" |
53 | #include "compat.h" |
54 | |
55 | namespace |
56 | { |
57 | |
58 | template <class T> |
59 | ssize_t getTypeSize(const T &t) |
60 | { |
61 | const clang::ASTContext &ctx = t->getASTContext(); |
62 | const clang::QualType &ty = ctx.getRecordType(t); |
63 | |
64 | /** Return size in bytes */ |
65 | return ctx.getTypeSize(ty) >> 3; |
66 | } |
67 | |
68 | /** |
69 | * XXX: avoid endless recursion inside |
70 | * clang::ASTContext::getTypeInfo() -> getTypeInfoImpl() |
71 | */ |
72 | template <class T> |
73 | bool cxxDeclIndependent(const T* decl) |
74 | { |
75 | const clang::CXXRecordDecl *cxx = llvm::dyn_cast<clang::CXXRecordDecl>(decl); |
76 | if (cxx && cxx->isDependentContext()) { |
77 | return false; |
78 | } |
79 | |
80 | /** Non CXX always independent */ |
81 | return true; |
82 | } |
83 | |
84 | ssize_t getDeclSize(const clang::Decl* decl) |
85 | { |
86 | const clang::CXXRecordDecl *cxx = llvm::dyn_cast<clang::CXXRecordDecl>(decl); |
87 | if (cxx && (cxx = cxx->getDefinition())) { |
88 | if (!cxxDeclIndependent(decl)) { |
89 | return -1; |
90 | } |
91 | return getTypeSize(cxx); |
92 | } |
93 | |
94 | const clang::RecordDecl *c = llvm::dyn_cast<clang::RecordDecl>(decl); |
95 | if (c && (c = c->getDefinition())) { |
96 | return getTypeSize(c); |
97 | } |
98 | |
99 | return -1; |
100 | } |
101 | |
102 | ssize_t getFieldOffset(const clang::Decl* decl) |
103 | { |
104 | const clang::FieldDecl* fd = llvm::dyn_cast<clang::FieldDecl>(decl); |
105 | if (!fd || fd->isInvalidDecl()) { |
106 | return -1; |
107 | } |
108 | |
109 | const clang::RecordDecl* parent = fd->getParent(); |
110 | if (!parent || parent->isInvalidDecl() || !cxxDeclIndependent(parent)) { |
111 | return -1; |
112 | } |
113 | |
114 | const clang::ASTRecordLayout &layout = decl->getASTContext().getASTRecordLayout(parent); |
115 | return layout.getFieldOffset(fd->getFieldIndex()); |
116 | } |
117 | |
118 | } |
119 | |
120 | Annotator::~Annotator() |
121 | { } |
122 | |
123 | Annotator::Visibility Annotator::getVisibility(const clang::NamedDecl *decl) |
124 | { |
125 | if (llvm::isa<clang::EnumConstantDecl>(decl) || |
126 | llvm::isa<clang::EnumDecl>(decl) || |
127 | llvm::isa<clang::NamespaceDecl>(decl) || |
128 | llvm::isa<clang::NamespaceAliasDecl>(decl) || |
129 | llvm::isa<clang::TypedefDecl>(decl) || |
130 | llvm::isa<clang::TypedefNameDecl>(decl)) { |
131 | |
132 | if (!decl->isDefinedOutsideFunctionOrMethod()) |
133 | return Visibility::Local; |
134 | if (decl->isInAnonymousNamespace()) |
135 | return Visibility::Static; |
136 | return Visibility::Global; //FIXME |
137 | } |
138 | |
139 | if (llvm::isa<clang::NonTypeTemplateParmDecl>(decl)) |
140 | return Visibility::Static; |
141 | |
142 | if (llvm::isa<clang::LabelDecl>(decl)) |
143 | return Visibility::Local; |
144 | |
145 | #if CLANG_VERSION_MAJOR >= 5 |
146 | if (llvm::isa<clang::CXXDeductionGuideDecl>(decl)) |
147 | return Visibility::Static; // Because it is not referenced in the AST anyway (FIXME) |
148 | #endif |
149 | |
150 | clang::SourceManager &sm = getSourceMgr(); |
151 | clang::FileID mainFID = sm.getMainFileID(); |
152 | |
153 | switch (decl->getLinkageInternal()) |
154 | { |
155 | default: |
156 | case clang::NoLinkage: |
157 | return Visibility::Local; |
158 | case clang::ExternalLinkage: |
159 | if (decl->getDeclContext()->isRecord() |
160 | && mainFID == sm.getFileID(sm.getSpellingLoc(llvm::dyn_cast<clang::NamedDecl>(decl->getDeclContext())->getCanonicalDecl()->getSourceRange().getBegin()))) { |
161 | // private class |
162 | const clang::CXXMethodDecl* fun = llvm::dyn_cast<clang::CXXMethodDecl>(decl); |
163 | if (fun && fun->isVirtual()) |
164 | return Visibility::Global; //because we need to check overrides |
165 | return Visibility::Static; |
166 | } |
167 | if (decl->isInvalidDecl() && llvm::isa<clang::VarDecl>(decl)) { |
168 | // Avoid polution because of invalid declarations |
169 | return Visibility::Static; |
170 | } |
171 | return Visibility::Global; |
172 | case clang::InternalLinkage: |
173 | if (mainFID != sm.getFileID(sm.getSpellingLoc(decl->getSourceRange().getBegin()))) |
174 | return Visibility::Global; |
175 | return Visibility::Static; |
176 | case clang::UniqueExternalLinkage: |
177 | return Visibility::Static; |
178 | } |
179 | } |
180 | |
181 | bool Annotator::shouldProcess(clang::FileID FID) |
182 | { |
183 | auto it = cache.find(FID); |
184 | if (it == cache.end()) { |
185 | htmlNameForFile(FID); |
186 | it = cache.find(FID); |
187 | assert(it != cache.end()); |
188 | } |
189 | return it->second.first; |
190 | } |
191 | |
192 | |
193 | std::string Annotator::htmlNameForFile(clang::FileID id) |
194 | { |
195 | { |
196 | auto it = cache.find(id); |
197 | if (it != cache.end()) { |
198 | return it->second.second; |
199 | } |
200 | } |
201 | |
202 | const clang::FileEntry* entry = getSourceMgr().getFileEntryForID(id); |
203 | if (!entry || llvm::StringRef(entry->getName()).empty()) { |
204 | cache[id] = {false, {} }; |
205 | return {}; |
206 | } |
207 | llvm::SmallString<256> filename; |
208 | canonicalize(entry->getName(), filename); |
209 | |
210 | ProjectInfo *project = projectManager.projectForFile(filename); |
211 | if (project) { |
212 | bool should_process = projectManager.shouldProcess(filename, project); |
213 | project_cache[id] = project; |
214 | std::string fn = project->name % "/" % filename.substr(project->source_path.size()); |
215 | cache[id] = { should_process , fn}; |
216 | return fn; |
217 | } |
218 | |
219 | cache[id] = {false, {} }; |
220 | return {}; |
221 | } |
222 | |
223 | static char normalizeForfnIndex(char c) { |
224 | if (c >= 'A' && c <= 'Z') |
225 | c = c - 'A' + 'a'; |
226 | if (c < 'a' || c > 'z') |
227 | return '_'; |
228 | return c; |
229 | } |
230 | |
231 | void Annotator::registerInterestingDefinition(clang::SourceRange sourceRange, clang::NamedDecl *decl) { |
232 | std::string declName = decl->getQualifiedNameAsString(); |
233 | clang::FileID fileId = sourceManager->getFileID(sourceRange.getBegin()); |
234 | auto &set = interestingDefinitionsInFile[fileId]; |
235 | set.insert(declName); |
236 | } |
237 | |
238 | bool Annotator::generate(clang::Sema &Sema, bool WasInDatabase) |
239 | { |
240 | std::ofstream fileIndex; |
241 | fileIndex.open(projectManager.outputPrefix + "/fileIndex" , std::ios::app); |
242 | if (!fileIndex) { |
243 | create_directories(projectManager.outputPrefix); |
244 | fileIndex.open(projectManager.outputPrefix + "/fileIndex" , std::ios::app); |
245 | if (!fileIndex) { |
246 | std::cerr << "Can't generate index for " << std::endl; |
247 | return false; |
248 | } |
249 | } |
250 | |
251 | // make sure the main file is in the cache. |
252 | htmlNameForFile(getSourceMgr().getMainFileID()); |
253 | |
254 | std::set<std::string> done; |
255 | for(auto it : cache) { |
256 | if (!it.second.first) |
257 | continue; |
258 | const std::string &fn = it.second.second; |
259 | if (done.count(fn)) |
260 | continue; |
261 | done.insert(fn); |
262 | |
263 | auto project_it = std::find_if(projectManager.projects.cbegin(), projectManager.projects.cend(), |
264 | [&fn](const ProjectInfo &it) |
265 | { return llvm::StringRef(fn).startswith(it.name); } ); |
266 | if (project_it == projectManager.projects.cend()) { |
267 | std::cerr << "GENERATION ERROR: " << fn << " not in a project" << std::endl; |
268 | continue; |
269 | } |
270 | |
271 | clang::FileID FID = it.first; |
272 | |
273 | Generator &g = generator(FID); |
274 | |
275 | syntaxHighlight(g, FID, Sema); |
276 | // clang::html::HighlightMacros(R, FID, PP); |
277 | |
278 | std::string ; |
279 | clang::FileID mainFID = getSourceMgr().getMainFileID(); |
280 | if (FID != mainFID) { |
281 | footer = "Generated while processing <a href='" % pathTo(FID, mainFID) % "'>" % htmlNameForFile(mainFID) % "</a><br/>" ; |
282 | } |
283 | |
284 | auto now = time(0); |
285 | auto tm = localtime(&now); |
286 | char buf[80]; |
287 | strftime(buf, sizeof(buf), "%Y-%b-%d" , tm); |
288 | |
289 | const ProjectInfo &projectinfo = *project_it; |
290 | footer %= "Generated on <em>" % std::string(buf) % "</em>" |
291 | % " from project " % projectinfo.name; |
292 | if (!projectinfo.revision.empty()) |
293 | footer %= " revision <em>" % projectinfo.revision % "</em>" ; |
294 | |
295 | /* << " from file <a href='" << projectinfo.fileRepoUrl(filename) << "'>" << filename << "</a>" |
296 | title=\"Arguments: << " << Generator::escapeAttr(args) <<"\"" */ |
297 | |
298 | // Emit the HTML. |
299 | const llvm::MemoryBuffer *Buf = getSourceMgr().getBuffer(FID); |
300 | g.generate(projectManager.outputPrefix, projectManager.dataPath, fn, |
301 | Buf->getBufferStart(), Buf->getBufferEnd(), footer, |
302 | WasInDatabase ? "" : "Warning: That file was not part of the compilation database. " |
303 | "It may have many parsing errors." , |
304 | interestingDefinitionsInFile[FID]); |
305 | |
306 | if (projectinfo.type == ProjectInfo::Normal) |
307 | fileIndex << fn << '\n'; |
308 | } |
309 | |
310 | // make sure all the docs are in the references |
311 | // (There might not be when the comment is in the .cpp file (for \class)) |
312 | for (auto it : commentHandler.docs) references[it.first]; |
313 | |
314 | create_directories(llvm::Twine(projectManager.outputPrefix, "/refs/_M" )); |
315 | for (const auto &it : references) { |
316 | if (llvm::StringRef(it.first).startswith("__builtin" )) |
317 | continue; |
318 | if (it.first == "main" ) |
319 | continue; |
320 | |
321 | auto refFilename = it.first; |
322 | replace_invalid_filename_chars(refFilename); |
323 | |
324 | std::string filename = projectManager.outputPrefix % "/refs/" % refFilename; |
325 | #if CLANG_VERSION_MAJOR==3 && CLANG_VERSION_MINOR<=5 |
326 | std::string error; |
327 | llvm::raw_fd_ostream myfile(filename.c_str(), error, llvm::sys::fs::F_Append); |
328 | if (!error.empty()) { |
329 | std::cerr << error<< std::endl; |
330 | continue; |
331 | } |
332 | #else |
333 | std::error_code error_code; |
334 | llvm::raw_fd_ostream myfile(filename, error_code, llvm::sys::fs::F_Append); |
335 | if (error_code) { |
336 | std::cerr << "Error writing ref file " << filename << ": " << error_code.message() << std::endl; |
337 | continue; |
338 | } |
339 | #endif |
340 | for (const auto &it2 : it.second) { |
341 | clang::SourceRange loc = it2.loc; |
342 | clang::SourceManager &sm = getSourceMgr(); |
343 | clang::SourceLocation expBegin = sm.getExpansionLoc(loc.getBegin()); |
344 | clang::SourceLocation expEnd = sm.getExpansionLoc(loc.getEnd()); |
345 | std::string fn = htmlNameForFile(sm.getFileID(expBegin)); |
346 | if (fn.empty()) |
347 | continue; |
348 | clang::PresumedLoc fixedBegin = sm.getPresumedLoc(expBegin); |
349 | clang::PresumedLoc fixedEnd = sm.getPresumedLoc(expEnd); |
350 | const char *tag = "" ; |
351 | char usetype = '\0'; |
352 | switch(it2.what) { |
353 | case Use: |
354 | case Use_NestedName: |
355 | tag = "use" ; |
356 | break; |
357 | case Use_Address: |
358 | tag = "use" ; |
359 | usetype = 'a'; |
360 | break; |
361 | case Use_Call: |
362 | tag = "use" ; |
363 | usetype = 'c'; |
364 | break; |
365 | case Use_Read: |
366 | tag = "use" ; |
367 | usetype = 'r'; |
368 | break; |
369 | case Use_Write: |
370 | tag = "use" ; |
371 | usetype = 'w'; |
372 | break; |
373 | case Use_MemberAccess: |
374 | tag = "use" ; |
375 | usetype = 'm'; |
376 | break; |
377 | case Declaration: |
378 | tag = "dec" ; |
379 | break; |
380 | case Definition: |
381 | tag = "def" ; |
382 | break; |
383 | case Override: |
384 | tag = "ovr" ; |
385 | break; |
386 | case Inherit: |
387 | tag = "inh" ; |
388 | } |
389 | myfile << "<" << tag << " f='" ; |
390 | Generator::escapeAttr(myfile, fn); |
391 | myfile << "' l='" << fixedBegin.getLine() <<"'" ; |
392 | if (fixedEnd.isValid() && fixedBegin.getLine() != fixedEnd.getLine()) |
393 | myfile << " ll='" << fixedEnd.getLine() <<"'" ; |
394 | if (loc.getBegin().isMacroID()) myfile << " macro='1'" ; |
395 | if (!WasInDatabase) myfile << " brk='1'" ; |
396 | if (usetype) myfile << " u='" << usetype << "'" ; |
397 | const auto &refType = it2.typeOrContext; |
398 | if (!refType.empty()) { |
399 | myfile << ((it2.what < Use) ? " type='" : " c='" ); |
400 | Generator::escapeAttr(myfile, refType); |
401 | myfile <<"'" ; |
402 | } |
403 | myfile <<"/>\n" ; |
404 | } |
405 | auto itS = structure_sizes.find(it.first); |
406 | if (itS != structure_sizes.end() && itS->second != -1) { |
407 | myfile << "<size>" << itS->second <<"</size>\n" ; |
408 | } |
409 | auto itF = field_offsets.find(it.first); |
410 | if (itF != field_offsets.end() && itF->second != -1) { |
411 | myfile << "<offset>" << itF->second <<"</offset>\n" ; |
412 | } |
413 | auto range = commentHandler.docs.equal_range(it.first); |
414 | for (auto it2 = range.first; it2 != range.second; ++it2) { |
415 | clang::SourceManager &sm = getSourceMgr(); |
416 | clang::SourceLocation exp = sm.getExpansionLoc(it2->second.loc); |
417 | clang::PresumedLoc fixed = sm.getPresumedLoc(exp); |
418 | std::string fn = htmlNameForFile(sm.getFileID(exp)); |
419 | myfile << "<doc f='" ; |
420 | Generator::escapeAttr(myfile, fn); |
421 | myfile << "' l='" << fixed.getLine() << "'>" ; |
422 | Generator::escapeAttr(myfile, it2->second.content); |
423 | myfile << "</doc>\n" ; |
424 | } |
425 | auto itU = sub_refs.find(it.first); |
426 | if (itU != sub_refs.end()) { |
427 | for (const auto &sub : itU->second) { |
428 | switch (sub.what) { |
429 | case SubRef::Function: myfile << "<fun " ; break; |
430 | case SubRef::Member: myfile << "<mbr " ; break; |
431 | case SubRef::Static: myfile << "<smbr " ; break; |
432 | case SubRef::None: continue; // should not happen |
433 | } |
434 | const auto &r = sub.ref; |
435 | myfile << "r='" << Generator::EscapeAttr{r} << "'" ; |
436 | auto itF = field_offsets.find(r); |
437 | if (itF != field_offsets.end() && itF->second != -1) |
438 | myfile << " o='" << itF->second << "'" ; |
439 | if (!sub.type.empty()) |
440 | myfile << " t='" << Generator::EscapeAttr{sub.type} << "'" ; |
441 | myfile << "/>\n" ; |
442 | } |
443 | } |
444 | } |
445 | |
446 | // now the function names |
447 | create_directories(llvm::Twine(projectManager.outputPrefix, "/fnSearch" )); |
448 | for(auto &fnIt : functionIndex) { |
449 | auto fnName = fnIt.first; |
450 | if (fnName.size() < 4) |
451 | continue; |
452 | if (fnName.find("__" ) != std::string::npos) |
453 | continue; // remove internals |
454 | if (fnName.find('<') != std::string::npos || fnName.find('>') != std::string::npos) |
455 | continue; // remove template stuff |
456 | if (fnName == "main" ) |
457 | continue; |
458 | |
459 | llvm::SmallString<8> saved; |
460 | auto pos = fnName.size() + 2; |
461 | int count = 0; |
462 | while (count < 2) { |
463 | count++; |
464 | if (pos < 4) |
465 | break; |
466 | pos = fnName.rfind("::" , pos-4); |
467 | if (pos >= fnName.size()) { |
468 | pos = 0; |
469 | } else { |
470 | pos += 2; // skip :: |
471 | } |
472 | char idx[3] = { normalizeForfnIndex(fnName[pos]), normalizeForfnIndex(fnName[pos+1]) , '\0' }; |
473 | llvm::StringRef idxRef(idx, 3); // include the '\0' on purpose |
474 | if (saved.find(idxRef) == std::string::npos) { |
475 | std::string funcIndexFN = projectManager.outputPrefix % "/fnSearch/" % idx; |
476 | #if CLANG_VERSION_MAJOR==3 && CLANG_VERSION_MINOR<=5 |
477 | std::string error; |
478 | llvm::raw_fd_ostream funcIndexFile(funcIndexFN.c_str(), error, llvm::sys::fs::F_Append); |
479 | if (!error.empty()) { |
480 | std::cerr << error << std::endl; |
481 | return false; |
482 | } |
483 | #else |
484 | std::error_code error_code; |
485 | llvm::raw_fd_ostream funcIndexFile(funcIndexFN, error_code, llvm::sys::fs::F_Append); |
486 | if (error_code) { |
487 | std::cerr << "Error writing index file " << funcIndexFN << ": " << error_code.message() << std::endl; |
488 | continue; |
489 | } |
490 | #endif |
491 | funcIndexFile << fnIt.second << '|'<< fnIt.first << '\n'; |
492 | saved.append(idxRef); //include \0; |
493 | } |
494 | } |
495 | } |
496 | return true; |
497 | } |
498 | |
499 | |
500 | std::string Annotator::pathTo(clang::FileID From, clang::FileID To, std::string *dataProj) |
501 | { |
502 | std::string &result = pathTo_cache[{From.getHashValue(), To.getHashValue()}]; |
503 | if (!result.empty()) { |
504 | if (dataProj) { |
505 | auto pr_it = project_cache.find(To); |
506 | if (pr_it != project_cache.end()) { |
507 | if (pr_it->second->type == ProjectInfo::External) { |
508 | *dataProj = pr_it->second->name; |
509 | } |
510 | } |
511 | } |
512 | return result; |
513 | } |
514 | |
515 | std::string fromFN = htmlNameForFile(From); |
516 | std::string toFN = htmlNameForFile(To); |
517 | |
518 | auto pr_it = project_cache.find(To); |
519 | if (pr_it == project_cache.end()) |
520 | return result = {}; |
521 | |
522 | if (pr_it->second->type == ProjectInfo::External) { |
523 | generator(From).addProject(pr_it->second->name, pr_it->second->external_root_url); |
524 | if (dataProj) { |
525 | *dataProj = pr_it->second->name % "\" " ; |
526 | } |
527 | return result = pr_it->second->external_root_url % "/" % toFN % ".html" ; |
528 | } |
529 | |
530 | return result = naive_uncomplete(llvm::sys::path::parent_path(fromFN), toFN) + ".html" ; |
531 | } |
532 | |
533 | std::string Annotator::pathTo(clang::FileID From, const clang::FileEntry *To) |
534 | { |
535 | //this is a bit duplicated with the other pathTo and htmlNameForFile |
536 | |
537 | if (!To || llvm::StringRef(To->getName()).empty()) |
538 | return {}; |
539 | |
540 | std::string fromFN = htmlNameForFile(From); |
541 | |
542 | llvm::SmallString<256> filename; |
543 | canonicalize(To->getName(), filename); |
544 | |
545 | |
546 | ProjectInfo *project = projectManager.projectForFile(filename); |
547 | if (!project) |
548 | return {}; |
549 | |
550 | if (project->type == ProjectInfo::External) { |
551 | return project->external_root_url % "/" % project->name % "/" % (filename.c_str() + project->source_path.size()) % ".html" ; |
552 | } |
553 | |
554 | return naive_uncomplete(llvm::sys::path::parent_path(fromFN), |
555 | std::string(project->name % "/" % (filename.c_str() + project->source_path.size()) % ".html" )); |
556 | } |
557 | |
558 | static const clang::Decl *getDefinitionDecl(clang::Decl *decl) { |
559 | if (const clang::RecordDecl* rec = llvm::dyn_cast<clang::RecordDecl>(decl)) { |
560 | rec = rec->getDefinition(); |
561 | if (rec) return rec; |
562 | } else if (const clang::FunctionDecl* fnc = llvm::dyn_cast<clang::FunctionDecl>(decl)) { |
563 | if (fnc->hasBody(fnc) && fnc) { |
564 | return fnc; |
565 | } |
566 | } |
567 | return decl->getCanonicalDecl(); |
568 | } |
569 | |
570 | void Annotator::registerReference(clang::NamedDecl* decl, clang::SourceRange range, Annotator::TokenType type, |
571 | Annotator::DeclType declType, std::string typeText, |
572 | clang::NamedDecl *usedContext) |
573 | { |
574 | //annonymouse namespace, anonymous struct, or unnamed argument. |
575 | if (decl->getDeclName().isIdentifier() && decl->getName().empty()) |
576 | return; |
577 | |
578 | clang::SourceManager &sm = getSourceMgr(); |
579 | |
580 | Visibility visibility = getVisibility(decl); |
581 | |
582 | // Interesting definitions |
583 | if (declType == Annotator::Definition && visibility == Visibility::Global) { |
584 | if (llvm::isa<clang::TagDecl>(decl) || (llvm::isa<clang::FunctionDecl>(decl) |
585 | && decl->getDeclName().isIdentifier() && decl->getName() == "main" )) { |
586 | if (decl->getDeclContext()->isNamespace() || decl->getDeclContext()->isTranslationUnit()) { |
587 | registerInterestingDefinition(range, decl); |
588 | } |
589 | } |
590 | } |
591 | |
592 | // When the end location is invalid, this is a virtual range with no matching tokens |
593 | // (eg implicit conversion) |
594 | bool isVirtualLocation = range.getEnd().isInvalid(); |
595 | if (isVirtualLocation) |
596 | range = range.getBegin(); |
597 | |
598 | if (!range.getBegin().isFileID()) { //macro expension. |
599 | clang::SourceLocation expensionloc = sm.getExpansionLoc(range.getBegin()); |
600 | clang::FileID FID = sm.getFileID(expensionloc); |
601 | if (!shouldProcess(FID) || sm.getMacroArgExpandedLocation(range.getBegin()) != |
602 | sm.getMacroArgExpandedLocation(range.getEnd())) { |
603 | return; |
604 | } |
605 | |
606 | clang::SourceLocation spel1 = sm.getSpellingLoc(range.getBegin()); |
607 | clang::SourceLocation spel2 = sm.getSpellingLoc(range.getEnd()); |
608 | if (sm.getFileID(spel1) != FID |
609 | || sm.getFileID(spel2) != FID) { |
610 | |
611 | if (visibility == Visibility::Global) { |
612 | if (usedContext && typeText.empty() && declType >= Use) { |
613 | typeText = getContextStr(usedContext); |
614 | } |
615 | addReference(getReferenceAndTitle(decl).first, range, type, declType, typeText, decl); |
616 | } |
617 | return; |
618 | } |
619 | |
620 | range = {spel1, spel2 }; |
621 | } |
622 | clang::FileID FID = sm.getFileID(range.getBegin()); |
623 | |
624 | if (!isVirtualLocation && FID != sm.getFileID(range.getEnd())) |
625 | return; |
626 | |
627 | if (!shouldProcess(FID)) |
628 | return; |
629 | |
630 | std::string tags; |
631 | std::string clas = computeClas(decl); |
632 | std::string ref; |
633 | |
634 | const clang::Decl* canonDecl = decl->getCanonicalDecl(); |
635 | if (type != Namespace) { |
636 | if (visibility == Visibility::Local) { |
637 | if (!decl->getDeclName().isIdentifier()) |
638 | return; //skip local operators (FIXME) |
639 | |
640 | clang::SourceLocation loc = canonDecl->getLocation(); |
641 | int &id = localeNumbers[loc.getRawEncoding()]; |
642 | if (id == 0) id = localeNumbers.size(); |
643 | llvm::StringRef name = decl->getName(); |
644 | ref = (llvm::Twine(id) + name).str(); |
645 | if (type != Label) { |
646 | llvm::SmallString<64> buffer; |
647 | tags %= " title='" % Generator::escapeAttr(name, buffer) % "'" ; |
648 | clas %= " local col" % llvm::Twine(id % 10).str(); |
649 | } |
650 | } else { |
651 | auto cached = getReferenceAndTitle(decl); |
652 | ref = cached.first; |
653 | tags %= " title='" % cached.second % "'" ; |
654 | } |
655 | |
656 | if (visibility == Visibility::Global && type != Typedef) { |
657 | if (usedContext && typeText.empty() && declType >= Use) { |
658 | typeText = getContextStr(usedContext); |
659 | } |
660 | |
661 | clang::SourceRange definitionRange = range; |
662 | if (declType == Definition) |
663 | definitionRange = decl->getSourceRange(); |
664 | addReference(ref, definitionRange, type, declType, typeText, decl); |
665 | |
666 | if (declType == Definition && ref.find('{') >= ref.size()) { |
667 | if (clang::FunctionDecl* fun = llvm::dyn_cast<clang::FunctionDecl>(decl)) { |
668 | functionIndex.insert({fun->getQualifiedNameAsString(), ref}); |
669 | } |
670 | } |
671 | } else { |
672 | if (!typeText.empty()) { |
673 | llvm::SmallString<64> buffer; |
674 | tags %= " data-type='" % Generator::escapeAttr(typeText, buffer) % "'" ; |
675 | } |
676 | } |
677 | |
678 | if (visibility == Visibility::Static) { |
679 | if (declType < Use) { |
680 | commentHandler.decl_offsets.insert({ decl->getSourceRange().getBegin(), {ref, false} }); |
681 | } else switch (+declType) { |
682 | case Use_Address: tags %= " data-use='a'" ; break; |
683 | case Use_Read: tags %= " data-use='r'" ; break; |
684 | case Use_Write: tags %= " data-use='w'" ; break; |
685 | case Use_Call: tags %= " data-use='c'" ; break; |
686 | case Use_MemberAccess: tags %= " data-use='m'" ; break; |
687 | } |
688 | |
689 | clas += " tu" ; |
690 | } |
691 | } |
692 | |
693 | switch(type) { |
694 | case Ref: clas += " ref" ; break; |
695 | case Member: clas += " member" ; break; |
696 | case Type: clas += " type" ; break; |
697 | case Typedef: clas += " typedef" ; break; |
698 | case Decl: clas += " decl" ; break; |
699 | case Call: clas += " call" ; break; |
700 | case Namespace: clas += " namespace" ; break; |
701 | case Enum: // fall through |
702 | case EnumDecl: clas += " enum" ; break; |
703 | case Label: clas += " lbl" ; break; |
704 | } |
705 | |
706 | if (declType == Definition && visibility != Visibility::Local) { |
707 | clas += " def" ; |
708 | } |
709 | |
710 | if (llvm::isa<clang::FunctionDecl>(decl)) { |
711 | clas += " fn" ; |
712 | } else if (llvm::isa<clang::FieldDecl>(decl)) { |
713 | clas += " field" ; |
714 | } |
715 | |
716 | // const llvm::MemoryBuffer *Buf = sm.getBuffer(FID); |
717 | clang::SourceLocation B = range.getBegin(); |
718 | clang::SourceLocation E = isVirtualLocation ? B : range.getEnd(); |
719 | |
720 | int pos = sm.getFileOffset(B); |
721 | int len = sm.getFileOffset(E) - pos; |
722 | |
723 | if (!isVirtualLocation) { |
724 | // Include the whole end token in the range. |
725 | len += clang::Lexer::MeasureTokenLength(E, sm, getLangOpts()); |
726 | } else { |
727 | clas += " fake" ; |
728 | } |
729 | |
730 | canonDecl = getDefinitionDecl(decl); |
731 | |
732 | if (clas[0] == ' ') clas = clas.substr(1); |
733 | |
734 | if (ref.empty()) { |
735 | generator(FID).addTag("span" , "class=\"" % clas % "\"" , pos, len); |
736 | return; |
737 | } |
738 | |
739 | llvm::SmallString<64> escapedRefBuffer; |
740 | auto escapedRef = Generator::escapeAttr(ref, escapedRefBuffer); |
741 | tags %= " data-ref=\"" % escapedRef % "\"" ; |
742 | |
743 | // Do some additional escaping for filenames (e.g., ':' is not valid on Windows) |
744 | llvm::SmallString<64> refFilenameBuffer; |
745 | auto refFilename = Generator::escapeAttrForFilename(escapedRef, refFilenameBuffer); |
746 | tags %= " data-ref-filename=\"" % refFilename % "\"" ; |
747 | |
748 | if (declType >= Annotator::Use || (decl != canonDecl && declType != Annotator::Definition) ) { |
749 | std::string link; |
750 | clang::SourceLocation loc = canonDecl->getLocation(); |
751 | clang::FileID declFID = sm.getFileID(sm.getExpansionLoc(loc)); |
752 | if (declFID != FID) { |
753 | std::string dataProj; |
754 | link = pathTo(FID, declFID, &dataProj); |
755 | |
756 | if (!dataProj.empty()) { |
757 | tags %= " data-proj=\"" % dataProj % "\"" ; |
758 | } |
759 | |
760 | if (declType < Annotator::Use) { |
761 | tags %= " id=\"" % escapedRef % "\"" ; |
762 | } |
763 | |
764 | if (link.empty()) { |
765 | generator(FID).addTag(declType >= Annotator::Use ? "span" : "dfn" , |
766 | "class=\'" % clas % "\'" % tags, pos, len); |
767 | return; |
768 | } |
769 | } |
770 | llvm::SmallString<6> locBuffer; |
771 | link %= "#" % (loc.isFileID() && !decl->isImplicit() ? |
772 | escapedRef : llvm::Twine(sm.getExpansionLineNumber(loc)).toStringRef(locBuffer)); |
773 | std::string tag = "class=\"" % clas % "\" href=\"" % link % "\"" % tags; |
774 | generator(FID).addTag("a" , tag, pos, len); |
775 | } else { |
776 | std::string tag = "class=\"" % clas % "\" id=\"" % escapedRef % "\"" % tags; |
777 | generator(FID).addTag("dfn" , tag, pos, len); |
778 | } |
779 | } |
780 | |
781 | void Annotator::addReference(const std::string &ref, clang::SourceRange refLoc, TokenType type, |
782 | DeclType dt, const std::string &typeRef, clang::Decl *decl) |
783 | { |
784 | if (type == Ref || type == Member || type == Decl || type == Call || type == EnumDecl |
785 | || (type == Type && dt != Use_NestedName && dt != Declaration) || (type == Enum && dt == Definition)) { |
786 | ssize_t size = getDeclSize(decl); |
787 | if (size >= 0) { |
788 | structure_sizes[ref] = size; |
789 | } |
790 | references[ref].push_back( { dt, refLoc, typeRef } ); |
791 | if (dt < Use) { |
792 | ssize_t offset = getFieldOffset(decl); |
793 | if (offset >= 0) { |
794 | field_offsets[ref] = offset; |
795 | } |
796 | clang::FullSourceLoc fulloc(decl->getSourceRange().getBegin(), getSourceMgr()); |
797 | commentHandler.decl_offsets.insert({ fulloc.getSpellingLoc(), {ref, true} }); |
798 | if (auto parentStruct = llvm::dyn_cast<clang::RecordDecl>(decl->getDeclContext())) { |
799 | auto parentRef = getReferenceAndTitle(parentStruct).first; |
800 | if (!parentRef.empty()) { |
801 | SubRef sr; |
802 | sr.ref = ref; |
803 | if (decl->isFunctionOrFunctionTemplate()) sr.what = SubRef::Function; |
804 | else if (llvm::isa<clang::FieldDecl>(decl)) sr.what = SubRef::Member; |
805 | else if (llvm::isa<clang::VarDecl>(decl)) sr.what = SubRef::Static; |
806 | if (sr.what != SubRef::Function) |
807 | sr.type = typeRef; |
808 | sub_refs[parentRef].push_back(sr); |
809 | } |
810 | } |
811 | } |
812 | } |
813 | } |
814 | |
815 | void Annotator::registerOverride(clang::NamedDecl* decl, clang::NamedDecl* overrided, clang::SourceLocation loc) |
816 | { |
817 | clang::SourceManager &sm = getSourceMgr(); |
818 | clang::SourceLocation expensionloc = sm.getExpansionLoc(loc); |
819 | clang::FileID FID = sm.getFileID(expensionloc); |
820 | if (!shouldProcess(FID)) |
821 | return; |
822 | if (getVisibility(overrided) != Visibility::Global) |
823 | return; |
824 | |
825 | auto ovrRef = getReferenceAndTitle(overrided).first; |
826 | auto declRef = getReferenceAndTitle(decl).first; |
827 | references[ovrRef].push_back( { Override, expensionloc, declRef } ); |
828 | |
829 | // Register the reversed relation. |
830 | clang::SourceLocation ovrLoc = sm.getExpansionLoc(getDefinitionDecl(overrided)->getLocation()); |
831 | references[declRef].push_back( { Inherit, ovrLoc, ovrRef } ); |
832 | } |
833 | |
834 | void Annotator::registerMacro(const std::string &ref, clang::SourceLocation refLoc, DeclType declType) |
835 | { |
836 | references[ref].push_back( { declType, refLoc, std::string() } ); |
837 | if (declType == Annotator::Declaration) { |
838 | commentHandler.decl_offsets.insert({ refLoc, {ref, true} }); |
839 | } |
840 | } |
841 | |
842 | void Annotator::annotateSourceRange(clang::SourceRange range, std::string tag, std::string attributes) |
843 | { |
844 | clang::SourceManager &sm = getSourceMgr(); |
845 | if (!range.getBegin().isFileID()) { |
846 | range = sm.getSpellingLoc(range.getBegin()); |
847 | if(!range.getBegin().isFileID()) |
848 | return; |
849 | } |
850 | |
851 | clang::FileID FID = sm.getFileID(range.getBegin()); |
852 | if (FID != sm.getFileID(range.getEnd())) { |
853 | return; |
854 | } |
855 | if (!shouldProcess(FID)) |
856 | return; |
857 | |
858 | |
859 | clang::SourceLocation B = range.getBegin(); |
860 | clang::SourceLocation E = range.getEnd(); |
861 | |
862 | unsigned int pos = sm.getFileOffset(B); |
863 | int len = sm.getFileOffset(E) - pos; |
864 | |
865 | // Include the whole end token in the range. |
866 | len += clang::Lexer::MeasureTokenLength(E, sm, getLangOpts()); |
867 | |
868 | generator(FID).addTag(std::move(tag), std::move(attributes), pos, len); |
869 | } |
870 | |
871 | void Annotator::reportDiagnostic(clang::SourceRange range, const std::string& msg, const std::string &clas) |
872 | { |
873 | llvm::SmallString<64> buffer; |
874 | annotateSourceRange(range, "span" , "class='" % clas % "' title=\"" % Generator::escapeAttr(msg, buffer) % "\"" ); |
875 | } |
876 | |
877 | //basically loosely inspired from clang_getSpecializedCursorTemplate |
878 | static clang::NamedDecl *getSpecializedCursorTemplate(clang::NamedDecl *D) { |
879 | using namespace clang; |
880 | using namespace llvm; |
881 | NamedDecl *Template = 0; |
882 | if (CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(D)) { |
883 | ClassTemplateDecl* CXXRecordT = 0; |
884 | if (ClassTemplatePartialSpecializationDecl *PartialSpec = dyn_cast<ClassTemplatePartialSpecializationDecl>(CXXRecord)) |
885 | CXXRecordT = PartialSpec->getSpecializedTemplate(); |
886 | else if (ClassTemplateSpecializationDecl *ClassSpec = dyn_cast<ClassTemplateSpecializationDecl>(CXXRecord)) { |
887 | llvm::PointerUnion<ClassTemplateDecl *, |
888 | ClassTemplatePartialSpecializationDecl *> Result |
889 | = ClassSpec->getSpecializedTemplateOrPartial(); |
890 | if (Result.is<ClassTemplateDecl *>()) |
891 | CXXRecordT = Result.get<ClassTemplateDecl *>(); |
892 | else |
893 | D = CXXRecord = Result.get<ClassTemplatePartialSpecializationDecl *>(); |
894 | } |
895 | if (CXXRecordT) |
896 | D = CXXRecord = CXXRecordT->getTemplatedDecl(); |
897 | Template = CXXRecord->getInstantiatedFromMemberClass(); |
898 | } else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) { |
899 | FunctionTemplateDecl* FunctionT = Function->getPrimaryTemplate(); |
900 | if (FunctionT) { |
901 | if (auto Ins = FunctionT->getInstantiatedFromMemberTemplate()) |
902 | FunctionT = Ins; |
903 | D = Function = FunctionT->getTemplatedDecl(); |
904 | } |
905 | Template = Function->getInstantiatedFromMemberFunction(); |
906 | } else if (VarDecl *Var = dyn_cast<VarDecl>(D)) { |
907 | if (Var->isStaticDataMember()) |
908 | Template = Var->getInstantiatedFromStaticDataMember(); |
909 | } else if (RedeclarableTemplateDecl *Tmpl = dyn_cast<RedeclarableTemplateDecl>(D)) { |
910 | Template = Tmpl->getInstantiatedFromMemberTemplate(); |
911 | } |
912 | |
913 | if (Template) return Template; |
914 | else return D; |
915 | } |
916 | |
917 | |
918 | std::pair< std::string, std::string > Annotator::getReferenceAndTitle(clang::NamedDecl* decl) |
919 | { |
920 | clang::Decl* canonDecl = decl->getCanonicalDecl(); |
921 | auto &cached = mangle_cache[canonDecl]; |
922 | if (cached.first.empty()) { |
923 | decl = getSpecializedCursorTemplate(decl); |
924 | |
925 | std::string qualName = decl->getQualifiedNameAsString(); |
926 | if (llvm::isa<clang::FunctionDecl>(decl) |
927 | #if CLANG_VERSION_MAJOR >= 5 |
928 | // We can't mangle a deduction guide (also there is no need since it is not referenced) |
929 | && !llvm::isa<clang::CXXDeductionGuideDecl>(decl) |
930 | #endif |
931 | && mangle->shouldMangleDeclName(decl) |
932 | //workaround crash in clang while trying to mangle some builtin types |
933 | && !llvm::StringRef(qualName).startswith("__" )) { |
934 | llvm::raw_string_ostream s(cached.first); |
935 | if (llvm::isa<clang::CXXDestructorDecl>(decl)) { |
936 | mangle->mangleCXXDtor(llvm::cast<clang::CXXDestructorDecl>(decl), clang::Dtor_Complete, s); |
937 | } else if (llvm::isa<clang::CXXConstructorDecl>(decl)) { |
938 | mangle->mangleCXXCtor(llvm::cast<clang::CXXConstructorDecl>(decl), clang::Ctor_Complete, s); |
939 | } else { |
940 | mangle->mangleName(decl, s); |
941 | } |
942 | |
943 | #ifdef _WIN32 |
944 | s.flush(); |
945 | |
946 | const char* mangledName = cached.first.data(); |
947 | if (mangledName[0] == 1) { |
948 | if(mangledName[1] == '_' || mangledName[1] == '?') { |
949 | if(mangledName[2] == '?') { |
950 | cached.first = cached.first.substr(3); |
951 | } else { |
952 | cached.first = cached.first.substr(2); |
953 | } |
954 | } |
955 | } |
956 | #endif |
957 | } else if (clang::FieldDecl *d = llvm::dyn_cast<clang::FieldDecl>(decl)) { |
958 | cached.first = getReferenceAndTitle(d->getParent()).first + "::" + decl->getName().str(); |
959 | } else { |
960 | cached.first = qualName; |
961 | cached.first.erase(std::remove(cached.first.begin(), cached.first.end(), ' '), |
962 | cached.first.end()); |
963 | // replace < and > because alse jquery can't match them. |
964 | std::replace(cached.first.begin(), cached.first.end(), '<' , '{'); |
965 | std::replace(cached.first.begin(), cached.first.end(), '>' , '}'); |
966 | } |
967 | llvm::SmallString<64> buffer; |
968 | cached.second = Generator::escapeAttr(qualName, buffer); |
969 | |
970 | if (cached.first.size() > 170) { |
971 | // If the name is too big, truncate it and add the hash at the end. |
972 | auto hash = std::hash<std::string>()(cached.first) & 0x00ffffff; |
973 | cached.first.resize(150); |
974 | buffer.clear(); |
975 | cached.first += llvm::Twine(hash).toStringRef(buffer); |
976 | } |
977 | } |
978 | return cached; |
979 | } |
980 | |
981 | |
982 | std::string Annotator::getTypeRef(clang::QualType type) |
983 | { |
984 | return type.getAsString(getLangOpts()); |
985 | } |
986 | |
987 | std::string Annotator::getContextStr(clang::NamedDecl* usedContext) |
988 | { |
989 | clang::FunctionDecl *fun = llvm::dyn_cast<clang::FunctionDecl>(usedContext); |
990 | clang::DeclContext* context = usedContext->getDeclContext(); |
991 | while(!fun && context) { |
992 | fun = llvm::dyn_cast<clang::FunctionDecl>(context); |
993 | if (fun && !fun->isDefinedOutsideFunctionOrMethod()) |
994 | fun = nullptr; |
995 | context = context->getParent(); |
996 | } |
997 | if (fun) |
998 | return getReferenceAndTitle(fun).first; |
999 | return {}; |
1000 | } |
1001 | |
1002 | std::string Annotator::getVisibleRef(clang::NamedDecl* Decl) |
1003 | { |
1004 | if (getVisibility(Decl) != Visibility::Global) |
1005 | return {}; |
1006 | return getReferenceAndTitle(Decl).first; |
1007 | } |
1008 | |
1009 | //return the classes to add in the span |
1010 | std::string Annotator::computeClas(clang::NamedDecl* decl) |
1011 | { |
1012 | std::string s; |
1013 | if (clang::CXXMethodDecl* f = llvm::dyn_cast<clang::CXXMethodDecl>(decl)) { |
1014 | if (f->isVirtual()) |
1015 | s = "virtual" ; |
1016 | } |
1017 | return s; |
1018 | } |
1019 | |
1020 | |
1021 | /* This function is inspired From clang::html::SyntaxHighlight() from HTMLRewrite.cpp |
1022 | * from the clang 3.1 from The LLVM Compiler Infrastructure |
1023 | * distributed under the University of Illinois Open Source |
1024 | * Adapted to the codebrowser generator. Also used to parse the comments. |
1025 | * The tags names have been changed, and we make a difference between different kinds of |
1026 | * keywords |
1027 | */ |
1028 | void Annotator::syntaxHighlight(Generator &generator, clang::FileID FID, clang::Sema &Sema) { |
1029 | using namespace clang; |
1030 | |
1031 | const clang::Preprocessor &PP = Sema.getPreprocessor(); |
1032 | const clang::SourceManager &SM = getSourceMgr(); |
1033 | const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); |
1034 | Lexer L(FID, FromFile, SM, getLangOpts()); |
1035 | const char *BufferStart = FromFile->getBufferStart(); |
1036 | const char *BufferEnd = FromFile->getBufferEnd(); |
1037 | |
1038 | // Inform the preprocessor that we want to retain comments as tokens, so we |
1039 | // can highlight them. |
1040 | L.SetCommentRetentionState(true); |
1041 | |
1042 | // Lex all the tokens in raw mode, to avoid entering #includes or expanding |
1043 | // macros. |
1044 | Token Tok; |
1045 | L.LexFromRawLexer(Tok); |
1046 | |
1047 | while (Tok.isNot(tok::eof)) { |
1048 | // Since we are lexing unexpanded tokens, all tokens are from the main |
1049 | // FileID. |
1050 | unsigned TokOffs = SM.getFileOffset(Tok.getLocation()); |
1051 | unsigned TokLen = Tok.getLength(); |
1052 | switch (Tok.getKind()) { |
1053 | default: break; |
1054 | case tok::identifier: |
1055 | llvm_unreachable("tok::identifier in raw lexing mode!" ); |
1056 | case tok::raw_identifier: { |
1057 | // Fill in Result.IdentifierInfo and update the token kind, |
1058 | // looking up the identifier in the identifier table. |
1059 | PP.LookUpIdentifierInfo(Tok); |
1060 | // If this is a pp-identifier, for a keyword, highlight it as such. |
1061 | switch (Tok.getKind()) { |
1062 | case tok::identifier: |
1063 | break; |
1064 | |
1065 | case tok::kw_auto: |
1066 | case tok::kw_char: |
1067 | case tok::kw_const: |
1068 | case tok::kw_double: |
1069 | case tok::kw_float: |
1070 | case tok::kw_int: |
1071 | case tok::kw_long: |
1072 | case tok::kw_register: |
1073 | // case tok::kw_restrict: // ??? (type or not) |
1074 | case tok::kw_short: |
1075 | case tok::kw_signed: |
1076 | case tok::kw_static: |
1077 | case tok::kw_unsigned: |
1078 | case tok::kw_void: |
1079 | case tok::kw_volatile: |
1080 | case tok::kw_bool: |
1081 | case tok::kw_mutable: |
1082 | case tok::kw_wchar_t: |
1083 | case tok::kw_char16_t: |
1084 | case tok::kw_char32_t: |
1085 | generator.addTag("em" , {}, TokOffs, TokLen); |
1086 | break; |
1087 | default: //other keywords |
1088 | generator.addTag("b" , {}, TokOffs, TokLen); |
1089 | } |
1090 | break; |
1091 | } |
1092 | case tok::comment: { |
1093 | unsigned int = TokOffs; |
1094 | unsigned int = TokLen; |
1095 | bool startOfLine = Tok.isAtStartOfLine(); |
1096 | SourceLocation = Tok.getLocation(); |
1097 | L.LexFromRawLexer(Tok); |
1098 | // Merge consecutive comments |
1099 | if (startOfLine /*&& BufferStart[CommentBegin+1] == '/'*/) { |
1100 | while (Tok.is(tok::comment)) { |
1101 | unsigned int Off = SM.getFileOffset(Tok.getLocation()); |
1102 | if (BufferStart[Off+1] != '/') |
1103 | break; |
1104 | CommentLen = Off + Tok.getLength() - CommentBegin; |
1105 | L.LexFromRawLexer(Tok); |
1106 | } |
1107 | } |
1108 | |
1109 | std::string attributes; |
1110 | |
1111 | if (startOfLine) { |
1112 | unsigned int = SM.getFileOffset(Tok.getLocation()); |
1113 | // Find the location of the next \n |
1114 | const char *nl_it = BufferStart + NonCommentBegin; |
1115 | while (nl_it < BufferEnd && *nl_it && *nl_it != '\n') |
1116 | ++nl_it; |
1117 | commentHandler.handleComment(*this, generator, Sema, BufferStart, CommentBegin, CommentLen, |
1118 | Tok.getLocation(), |
1119 | Tok.getLocation().getLocWithOffset(nl_it - (BufferStart + NonCommentBegin)), |
1120 | CommentBeginLocation); |
1121 | } else { |
1122 | //look up the location before |
1123 | const char *nl_it = BufferStart + CommentBegin; |
1124 | while (nl_it > BufferStart && *nl_it && *nl_it != '\n') |
1125 | --nl_it; |
1126 | commentHandler.handleComment(*this, generator, Sema, BufferStart, CommentBegin, CommentLen, |
1127 | CommentBeginLocation.getLocWithOffset(nl_it - (BufferStart + CommentBegin)), |
1128 | CommentBeginLocation, CommentBeginLocation); |
1129 | } |
1130 | continue; //Don't skip next token |
1131 | } |
1132 | case tok::utf8_string_literal: |
1133 | // Chop off the u part of u8 prefix |
1134 | ++TokOffs; |
1135 | --TokLen; |
1136 | LLVM_FALLTHROUGH; |
1137 | case tok::wide_string_literal: |
1138 | case tok::utf16_string_literal: |
1139 | case tok::utf32_string_literal: |
1140 | // Chop off the L, u, U or 8 prefix |
1141 | ++TokOffs; |
1142 | --TokLen; |
1143 | LLVM_FALLTHROUGH; |
1144 | case tok::string_literal: |
1145 | // FIXME: Exclude the optional ud-suffix from the highlighted range. |
1146 | generator.addTag("q" , {}, TokOffs, TokLen); |
1147 | break; |
1148 | |
1149 | case tok::wide_char_constant: |
1150 | case tok::utf16_char_constant: |
1151 | case tok::utf32_char_constant: |
1152 | ++TokOffs; |
1153 | --TokLen; |
1154 | LLVM_FALLTHROUGH; |
1155 | case tok::char_constant: |
1156 | generator.addTag("kbd" , {}, TokOffs, TokLen); |
1157 | break; |
1158 | case tok::numeric_constant: |
1159 | generator.addTag("var" , {}, TokOffs, TokLen); |
1160 | break; |
1161 | case tok::hash: { |
1162 | // If this is a preprocessor directive, all tokens to end of line are too. |
1163 | if (!Tok.isAtStartOfLine()) |
1164 | break; |
1165 | |
1166 | // Eat all of the tokens until we get to the next one at the start of |
1167 | // line. |
1168 | unsigned TokEnd = TokOffs+TokLen; |
1169 | L.LexFromRawLexer(Tok); |
1170 | while (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) { |
1171 | TokEnd = SM.getFileOffset(Tok.getLocation())+Tok.getLength(); |
1172 | L.LexFromRawLexer(Tok); |
1173 | } |
1174 | |
1175 | generator.addTag("u" , {}, TokOffs, TokEnd - TokOffs); |
1176 | |
1177 | // Don't skip the next token. |
1178 | continue; |
1179 | } |
1180 | } |
1181 | |
1182 | L.LexFromRawLexer(Tok); |
1183 | } |
1184 | } |
1185 | |