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