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

source code of codebrowser/generator/annotator.cpp