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 "TableGenBackends.h"
14#include "llvm/ADT/StringMap.h"
15#include "llvm/TableGen/Error.h"
16#include "llvm/TableGen/Record.h"
17#include "llvm/TableGen/TableGenBackend.h"
18#include <map>
19#include <string>
20
21using namespace llvm;
22
23//===----------------------------------------------------------------------===//
24// Static Analyzer Checkers Tables generation
25//===----------------------------------------------------------------------===//
26
27static std::string getPackageFullName(const Record *R, StringRef Sep = ".");
28
29static std::string getParentPackageFullName(const Record *R,
30                                            StringRef Sep = ".") {
31  std::string name;
32  if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
33    name = getPackageFullName(DI->getDef(), Sep);
34  return name;
35}
36
37static std::string getPackageFullName(const Record *R, StringRef Sep) {
38  std::string name = getParentPackageFullName(R, Sep);
39  if (!name.empty())
40    name += Sep;
41  assert(!R->getValueAsString("PackageName").empty());
42  name += R->getValueAsString("PackageName");
43  return name;
44}
45
46static std::string getCheckerFullName(const Record *R, StringRef Sep = ".") {
47  std::string name = getParentPackageFullName(R, Sep);
48  if (!name.empty())
49    name += Sep;
50  assert(!R->getValueAsString("CheckerName").empty());
51  name += R->getValueAsString("CheckerName");
52  return name;
53}
54
55static std::string getStringValue(const Record &R, StringRef field) {
56  if (StringInit *SI = dyn_cast<StringInit>(R.getValueInit(field)))
57    return std::string(SI->getValue());
58  return std::string();
59}
60
61// Calculates the integer value representing the BitsInit object
62static inline uint64_t getValueFromBitsInit(const BitsInit *B, const Record &R) {
63  assert(B->getNumBits() <= sizeof(uint64_t) * 8 && "BitInits' too long!");
64
65  uint64_t Value = 0;
66  for (unsigned i = 0, e = B->getNumBits(); i != e; ++i) {
67    const auto *Bit = dyn_cast<BitInit>(B->getBit(i));
68    if (Bit)
69      Value |= uint64_t(Bit->getValue()) << i;
70    else
71      PrintFatalError(R.getLoc(),
72                      "missing Documentation for " + getCheckerFullName(&R));
73  }
74  return Value;
75}
76
77static std::string getCheckerDocs(const Record &R) {
78  const BitsInit *BI = R.getValueAsBitsInit("Documentation");
79  if (!BI)
80    PrintFatalError(R.getLoc(), "missing Documentation<...> member for " +
81                                    getCheckerFullName(&R));
82
83  // Ignore 'Documentation<NotDocumented>' checkers.
84  if (getValueFromBitsInit(BI, R) == 0)
85    return "";
86
87  std::string CheckerFullName = StringRef(getCheckerFullName(&R, "-")).lower();
88  return (llvm::Twine("https://clang.llvm.org/docs/analyzer/checkers.html#") +
89          CheckerFullName)
90      .str();
91}
92
93/// Retrieves the type from a CmdOptionTypeEnum typed Record object. Note that
94/// the class itself has to be modified for adding a new option type in
95/// CheckerBase.td.
96static std::string getCheckerOptionType(const Record &R) {
97  if (BitsInit *BI = R.getValueAsBitsInit("Type")) {
98    switch(getValueFromBitsInit(BI, R)) {
99    case 0:
100      return "int";
101    case 1:
102      return "string";
103    case 2:
104      return "bool";
105    }
106  }
107  PrintFatalError(R.getLoc(),
108                  "unable to parse command line option type for "
109                  + getCheckerFullName(&R));
110  return "";
111}
112
113static std::string getDevelopmentStage(const Record &R) {
114  if (BitsInit *BI = R.getValueAsBitsInit("DevelopmentStage")) {
115    switch(getValueFromBitsInit(BI, R)) {
116    case 0:
117      return "alpha";
118    case 1:
119      return "released";
120    }
121  }
122
123  PrintFatalError(R.getLoc(),
124                  "unable to parse command line option type for "
125                  + getCheckerFullName(&R));
126  return "";
127}
128
129static bool isHidden(const Record *R) {
130  if (R->getValueAsBit("Hidden"))
131    return true;
132
133  // Not declared as hidden, check the parent package if it is hidden.
134  if (DefInit *DI = dyn_cast<DefInit>(R->getValueInit("ParentPackage")))
135    return isHidden(DI->getDef());
136
137  return false;
138}
139
140static void printChecker(llvm::raw_ostream &OS, const Record &R) {
141  OS << "CHECKER(" << "\"";
142  OS.write_escaped(getCheckerFullName(&R)) << "\", ";
143  OS << R.getName() << ", ";
144  OS << "\"";
145  OS.write_escaped(getStringValue(R, "HelpText")) << "\", ";
146  OS << "\"";
147  OS.write_escaped(getCheckerDocs(R));
148  OS << "\", ";
149
150  if (!isHidden(&R))
151    OS << "false";
152  else
153    OS << "true";
154
155  OS << ")\n";
156}
157
158static void printOption(llvm::raw_ostream &OS, StringRef FullName,
159                        const Record &R) {
160  OS << "\"";
161  OS.write_escaped(getCheckerOptionType(R)) << "\", \"";
162  OS.write_escaped(FullName) << "\", ";
163  OS << '\"' << getStringValue(R, "CmdFlag") << "\", ";
164  OS << '\"';
165  OS.write_escaped(getStringValue(R, "Desc")) << "\", ";
166  OS << '\"';
167  OS.write_escaped(getStringValue(R, "DefaultVal")) << "\", ";
168  OS << '\"';
169  OS << getDevelopmentStage(R) << "\", ";
170
171  if (!R.getValueAsBit("Hidden"))
172    OS << "false";
173  else
174    OS << "true";
175}
176
177void clang::EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
178  std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker");
179  std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package");
180
181  using SortedRecords = llvm::StringMap<const Record *>;
182
183  OS << "// This file is automatically generated. Do not edit this file by "
184        "hand.\n";
185
186  // Emit packages.
187  //
188  // PACKAGE(PACKAGENAME)
189  //   - PACKAGENAME: The name of the package.
190  OS << "\n"
191        "#ifdef GET_PACKAGES\n";
192  {
193    SortedRecords sortedPackages;
194    for (unsigned i = 0, e = packages.size(); i != e; ++i)
195      sortedPackages[getPackageFullName(packages[i])] = packages[i];
196
197    for (SortedRecords::iterator
198           I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) {
199      const Record &R = *I->second;
200
201      OS << "PACKAGE(" << "\"";
202      OS.write_escaped(getPackageFullName(&R)) << '\"';
203      OS << ")\n";
204    }
205  }
206  OS << "#endif // GET_PACKAGES\n"
207        "\n";
208
209  // Emit a package option.
210  //
211  // PACKAGE_OPTION(OPTIONTYPE, PACKAGENAME, OPTIONNAME, DESCRIPTION, DEFAULT)
212  //   - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.
213  //                 This is important for validating user input. Note that
214  //                 it's a string, rather than an actual type: since we can
215  //                 load checkers runtime, we can't use template hackery for
216  //                 sorting this out compile-time.
217  //   - PACKAGENAME: Name of the package.
218  //   - OPTIONNAME: Name of the option.
219  //   - DESCRIPTION
220  //   - DEFAULT: The default value for this option.
221  //
222  // The full option can be specified in the command like this:
223  //   -analyzer-config PACKAGENAME:OPTIONNAME=VALUE
224  OS << "\n"
225        "#ifdef GET_PACKAGE_OPTIONS\n";
226  for (const Record *Package : packages) {
227
228    if (Package->isValueUnset("PackageOptions"))
229      continue;
230
231    std::vector<Record *> PackageOptions = Package
232                                       ->getValueAsListOfDefs("PackageOptions");
233    for (Record *PackageOpt : PackageOptions) {
234      OS << "PACKAGE_OPTION(";
235      printOption(OS, getPackageFullName(Package), *PackageOpt);
236      OS << ")\n";
237    }
238  }
239  OS << "#endif // GET_PACKAGE_OPTIONS\n"
240        "\n";
241
242  // Emit checkers.
243  //
244  // CHECKER(FULLNAME, CLASS, HELPTEXT)
245  //   - FULLNAME: The full name of the checker, including packages, e.g.:
246  //               alpha.cplusplus.UninitializedObject
247  //   - CLASS: The name of the checker, with "Checker" appended, e.g.:
248  //            UninitializedObjectChecker
249  //   - HELPTEXT: The description of the checker.
250  OS << "\n"
251        "#ifdef GET_CHECKERS\n"
252        "\n";
253  for (const Record *checker : checkers) {
254    printChecker(OS, *checker);
255  }
256  OS << "\n"
257        "#endif // GET_CHECKERS\n"
258        "\n";
259
260  // Emit dependencies.
261  //
262  // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
263  //   - FULLNAME: The full name of the checker that depends on another checker.
264  //   - DEPENDENCY: The full name of the checker FULLNAME depends on.
265  OS << "\n"
266        "#ifdef GET_CHECKER_DEPENDENCIES\n";
267  for (const Record *Checker : checkers) {
268    if (Checker->isValueUnset("Dependencies"))
269      continue;
270
271    for (const Record *Dependency :
272                            Checker->getValueAsListOfDefs("Dependencies")) {
273      OS << "CHECKER_DEPENDENCY(";
274      OS << '\"';
275      OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
276      OS << '\"';
277      OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
278      OS << ")\n";
279    }
280  }
281  OS << "\n"
282        "#endif // GET_CHECKER_DEPENDENCIES\n";
283
284  // Emit weak dependencies.
285  //
286  // CHECKER_DEPENDENCY(FULLNAME, DEPENDENCY)
287  //   - FULLNAME: The full name of the checker that is supposed to be
288  //     registered first.
289  //   - DEPENDENCY: The full name of the checker FULLNAME weak depends on.
290  OS << "\n"
291        "#ifdef GET_CHECKER_WEAK_DEPENDENCIES\n";
292  for (const Record *Checker : checkers) {
293    if (Checker->isValueUnset("WeakDependencies"))
294      continue;
295
296    for (const Record *Dependency :
297         Checker->getValueAsListOfDefs("WeakDependencies")) {
298      OS << "CHECKER_WEAK_DEPENDENCY(";
299      OS << '\"';
300      OS.write_escaped(getCheckerFullName(Checker)) << "\", ";
301      OS << '\"';
302      OS.write_escaped(getCheckerFullName(Dependency)) << '\"';
303      OS << ")\n";
304    }
305  }
306  OS << "\n"
307        "#endif // GET_CHECKER_WEAK_DEPENDENCIES\n";
308
309  // Emit a package option.
310  //
311  // CHECKER_OPTION(OPTIONTYPE, CHECKERNAME, OPTIONNAME, DESCRIPTION, DEFAULT)
312  //   - OPTIONTYPE: Type of the option, whether it's integer or boolean etc.
313  //                 This is important for validating user input. Note that
314  //                 it's a string, rather than an actual type: since we can
315  //                 load checkers runtime, we can't use template hackery for
316  //                 sorting this out compile-time.
317  //   - CHECKERNAME: Name of the package.
318  //   - OPTIONNAME: Name of the option.
319  //   - DESCRIPTION
320  //   - DEFAULT: The default value for this option.
321  //
322  // The full option can be specified in the command like this:
323  //   -analyzer-config CHECKERNAME:OPTIONNAME=VALUE
324  OS << "\n"
325        "#ifdef GET_CHECKER_OPTIONS\n";
326  for (const Record *Checker : checkers) {
327
328    if (Checker->isValueUnset("CheckerOptions"))
329      continue;
330
331    std::vector<Record *> CheckerOptions = Checker
332                                       ->getValueAsListOfDefs("CheckerOptions");
333    for (Record *CheckerOpt : CheckerOptions) {
334      OS << "CHECKER_OPTION(";
335      printOption(OS, getCheckerFullName(Checker), *CheckerOpt);
336      OS << ")\n";
337    }
338  }
339  OS << "#endif // GET_CHECKER_OPTIONS\n"
340        "\n";
341}
342