1 //=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*-
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This tablegen backend emits Clang Static Analyzer checkers tables.
11 //
12 //===----------------------------------------------------------------------===//
13 
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 
21 using namespace llvm;
22 
23 //===----------------------------------------------------------------------===//
24 // Static Analyzer Checkers Tables generation
25 //===----------------------------------------------------------------------===//
26 
27 static std::string getPackageFullName(const Record *R);
28 
29 static 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 
36 static 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 
45 static 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 
54 static 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
61 static 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 
76 static 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 namespace clang {
95 void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) {
96   std::vector<Record*> checkers = Records.getAllDerivedDefinitions("Checker");
97   std::vector<Record*> packages = Records.getAllDerivedDefinitions("Package");
98 
99   using SortedRecords = llvm::StringMap<const Record *>;
100 
101   OS << "// This file is automatically generated. Do not edit this file by "
102         "hand.\n";
103 
104   OS << "\n#ifdef GET_PACKAGES\n";
105   {
106     SortedRecords sortedPackages;
107     for (unsigned i = 0, e = packages.size(); i != e; ++i)
108       sortedPackages[getPackageFullName(packages[i])] = packages[i];
109 
110     for (SortedRecords::iterator
111            I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) {
112       const Record &R = *I->second;
113 
114       OS << "PACKAGE(" << "\"";
115       OS.write_escaped(getPackageFullName(&R)) << '\"';
116       OS << ")\n";
117     }
118   }
119   OS << "#endif // GET_PACKAGES\n\n";
120 
121   OS << "\n#ifdef GET_CHECKERS\n";
122   for (unsigned i = 0, e = checkers.size(); i != e; ++i) {
123     const Record &R = *checkers[i];
124 
125     OS << "CHECKER(" << "\"";
126     OS.write_escaped(getCheckerFullName(&R)) << "\", ";
127     OS << R.getName() << ", ";
128     OS << "\"";
129     OS.write_escaped(getStringValue(R, "HelpText")) << "\", ";
130     OS << "\"";
131     OS.write_escaped(getCheckerDocs(R));
132     OS << "\"";
133     OS << ")\n";
134   }
135   OS << "#endif // GET_CHECKERS\n\n";
136 }
137 } // end namespace clang
138