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 
20 using namespace llvm;
21 
22 //===----------------------------------------------------------------------===//
23 // Static Analyzer Checkers Tables generation
24 //===----------------------------------------------------------------------===//
25 
26 static std::string getPackageFullName(const Record *R);
27 
28 static 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 
35 static 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 
44 static 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 
53 static 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
60 static inline uint64_t getValueFromBitsInit(const BitsInit *B, const Record &R) {
61   assert(B->getNumBits() <= sizeof(uint64_t) * 8 && "BitInits' too long!");
62 
63   uint64_t Value = 0;
64   for (unsigned i = 0, e = 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 
75 static 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 
93 static void printChecker(llvm::raw_ostream &OS, const 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 
104 namespace clang {
105 void 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