Clang Project

clang_source_code/utils/TableGen/ClangSACheckersEmitter.cpp
1//=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*-
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This tablegen backend emits Clang Static Analyzer checkers tables.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/ADT/StringMap.h"
14#include "llvm/TableGen/Error.h"
15#include "llvm/TableGen/Record.h"
16#include "llvm/TableGen/TableGenBackend.h"
17#include <map>
18#include <string>
19
20using namespace llvm;
21
22//===----------------------------------------------------------------------===//
23// Static Analyzer Checkers Tables generation
24//===----------------------------------------------------------------------===//
25
26static std::string getPackageFullName(const Record *R);
27
28static std::string getParentPackageFullName(const Record *R) {
29  std::string name;
30  if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
31    name = getPackageFullName(DI->getDef());
32  return name;
33}
34
35static std::string getPackageFullName(const Record *R) {
36  std::string name = getParentPackageFullName(R);
37  if (!name.empty())
38    name += ".";
39  assert(!R->getValueAsString("PackageName").empty());
40  name += R->getValueAsString("PackageName");
41  return name;
42}
43
44static std::string getCheckerFullName(const Record *R) {
45  std::string name = getParentPackageFullName(R);
46  if (!name.empty())
47    name += ".";
48  assert(!R->getValueAsString("CheckerName").empty());
49  name += R->getValueAsString("CheckerName");
50  return name;
51}
52
53static std::string getStringValue(const Record &R, StringRef field) {
54  if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field)))
55    return SI->getValue();
56  return std::string();
57}
58
59// Calculates the integer value representing the BitsInit object
60static inline uint64_t getValueFromBitsInit(const BitsInit *Bconst Record &R) {
61  assert(B->getNumBits() <= sizeof(uint64_t) * 8 && "BitInits' too long!");
62
63  uint64_t Value = 0;
64  for (unsigned i = 0e = B->getNumBits(); i != e; ++i) {
65    const auto *Bit = dyn_cast<BitInit>(B->getBit(i));
66    if (Bit)
67      Value |= uint64_t(Bit->getValue()) << i;
68    else
69      PrintFatalError(R.getLoc(),
70                      "missing Documentation for " + getCheckerFullName(&R));
71  }
72  return Value;
73}
74
75static std::string getCheckerDocs(const Record &R) {
76  StringRef LandingPage;
77  if (BitsInit *BI = R.getValueAsBitsInit("Documentation")) {
78    uint64_t V = getValueFromBitsInit(BI, R);
79    if (V == 1)
80      LandingPage = "available_checks.html";
81    else if (V == 2)
82      LandingPage = "alpha_checks.html";
83  }
84  
85  if (LandingPage.empty())
86    return "";
87
88  return (llvm::Twine("https://clang-analyzer.llvm.org/") + LandingPage + "#" +
89          getCheckerFullName(&R))
90      .str();
91}
92
93static void printChecker(llvm::raw_ostream &OSconst Record &R) {
94    OS << "CHECKER(" << "\"";
95    OS.write_escaped(getCheckerFullName(&R)) << "\", ";
96    OS << R.getName() << ", ";
97    OS << "\"";
98    OS.write_escaped(getStringValue(R, "HelpText")) << "\", ";
99    OS << "\"";
100    OS.write_escaped(getCheckerDocs(R));
101    OS << "\"";
102}
103
104namespace clang {
105void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
106  std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker");
107  std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package");
108
109  using SortedRecords = llvm::StringMap<const Record *>;
110
111  OS << "// This file is automatically generated. Do not edit this file by "
112        "hand.\n";
113
114  // Emit packages.
115  //
116  // PACKAGE(PACKAGENAME)
117  //   - PACKAGENAME: The name of the package.
118  OS << "\n"
119        "#ifdef GET_PACKAGES\n";
120  {
121    SortedRecords sortedPackages;
122    for (unsigned i = 0, e = packages.size(); i != e; ++i)
123      sortedPackages[getPackageFullName(packages[i])] = packages[i];
124  
125    for (SortedRecords::iterator
126           I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) {
127      const Record &R = *I->second;
128  
129      OS << "PACKAGE(" << "\"";
130      OS.write_escaped(getPackageFullName(&R)) << '\"';
131      OS << ")\n";
132    }
133  }
134  OS << "#endif // GET_PACKAGES\n"
135        "\n";
136
137  // Emit checkers.
138  //
139  // CHECKER(FULLNAME, CLASS, HELPTEXT)
140  //   - FULLNAME: The full name of the checker, including packages, e.g.:
141  //               alpha.cplusplus.UninitializedObject
142  //   - CLASS: The name of the checker, with "Checker" appended, e.g.:
143  //            UninitializedObjectChecker
144  //   - HELPTEXT: The description of the checker.
145  OS << "\n"
146        "#ifdef GET_CHECKERS\n"
147        "\n";
148  for (const Record *checker : checkers) {
149    printChecker(OS, *checker);
150    OS << ")\n";
151  }
152  OS << "\n"
153        "#endif // GET_CHECKERS\n"
154        "\n";
155
156  // Emit dependencies.
157  //
158  // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
159  //   - FULLNAME: The full name of the checker that depends on another checker.
160  //   - DEPENDENCY: The full name of the checker FULLNAME depends on.
161  OS << "\n"
162        "#ifdef GET_CHECKER_DEPENDENCIES\n";
163  for (const Record *checker : checkers) {
164    if (checker->isValueUnset("Dependencies"))
165      continue;
166
167    for (const Record *Dependency :
168                            checker->getValueAsListOfDefs("Dependencies")) {
169      OS << "CHECKER_DEPENDENCY(";
170      OS << '\"';
171      OS.write_escaped(getCheckerFullName(checker)) << "\", ";
172      OS << '\"';
173      OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
174      OS << ")\n";
175    }
176  }
177  OS << "\n"
178        "#endif // GET_CHECKER_DEPENDENCIES\n";
179}
180// end namespace clang
181