//=- ClangSACheckersEmitter.cpp - Generate Clang SA checkers tables -*- C++ -*- // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This tablegen backend emits Clang Static Analyzer checkers tables. // //===----------------------------------------------------------------------===// #include "llvm/ADT/DenseSet.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/TableGenBackend.h" #include #include using namespace llvm; //===----------------------------------------------------------------------===// // Static Analyzer Checkers Tables generation //===----------------------------------------------------------------------===// /// \brief True if it is specified hidden or a parent package is specified /// as hidden, otherwise false. static bool isHidden(const Record &R) { if (R.getValueAsBit("Hidden")) return true; // Not declared as hidden, check the parent package if it is hidden. if (DefInit *DI = dyn_cast(R.getValueInit("ParentPackage"))) return isHidden(*DI->getDef()); return false; } static bool isCheckerNamed(const Record *R) { return !R->getValueAsString("CheckerName").empty(); } static std::string getPackageFullName(const Record *R); static std::string getParentPackageFullName(const Record *R) { std::string name; if (DefInit *DI = dyn_cast(R->getValueInit("ParentPackage"))) name = getPackageFullName(DI->getDef()); return name; } static std::string getPackageFullName(const Record *R) { std::string name = getParentPackageFullName(R); if (!name.empty()) name += "."; return name + R->getValueAsString("PackageName"); } static std::string getCheckerFullName(const Record *R) { std::string name = getParentPackageFullName(R); if (isCheckerNamed(R)) { if (!name.empty()) name += "."; name += R->getValueAsString("CheckerName"); } return name; } static std::string getStringValue(const Record &R, StringRef field) { if (StringInit *SI = dyn_cast(R.getValueInit(field))) return SI->getValue(); return std::string(); } namespace { struct GroupInfo { llvm::DenseSet Checkers; llvm::DenseSet SubGroups; bool Hidden; unsigned Index; GroupInfo() : Hidden(false) { } }; } static void addPackageToCheckerGroup(const Record *package, const Record *group, llvm::DenseMap &recordGroupMap) { llvm::DenseSet &checkers = recordGroupMap[package]->Checkers; for (llvm::DenseSet::iterator I = checkers.begin(), E = checkers.end(); I != E; ++I) recordGroupMap[group]->Checkers.insert(*I); llvm::DenseSet &subGroups = recordGroupMap[package]->SubGroups; for (llvm::DenseSet::iterator I = subGroups.begin(), E = subGroups.end(); I != E; ++I) addPackageToCheckerGroup(*I, group, recordGroupMap); } namespace clang { void EmitClangSACheckers(RecordKeeper &Records, raw_ostream &OS) { std::vector checkers = Records.getAllDerivedDefinitions("Checker"); llvm::DenseMap checkerRecIndexMap; for (unsigned i = 0, e = checkers.size(); i != e; ++i) checkerRecIndexMap[checkers[i]] = i; // Invert the mapping of checkers to package/group into a one to many // mapping of packages/groups to checkers. std::map groupInfoByName; llvm::DenseMap recordGroupMap; std::vector packages = Records.getAllDerivedDefinitions("Package"); for (unsigned i = 0, e = packages.size(); i != e; ++i) { Record *R = packages[i]; std::string fullName = getPackageFullName(R); if (!fullName.empty()) { GroupInfo &info = groupInfoByName[fullName]; info.Hidden = isHidden(*R); recordGroupMap[R] = &info; } } std::vector checkerGroups = Records.getAllDerivedDefinitions("CheckerGroup"); for (unsigned i = 0, e = checkerGroups.size(); i != e; ++i) { Record *R = checkerGroups[i]; std::string name = R->getValueAsString("GroupName"); if (!name.empty()) { GroupInfo &info = groupInfoByName[name]; recordGroupMap[R] = &info; } } for (unsigned i = 0, e = checkers.size(); i != e; ++i) { Record *R = checkers[i]; Record *package = nullptr; if (DefInit * DI = dyn_cast(R->getValueInit("ParentPackage"))) package = DI->getDef(); if (!isCheckerNamed(R) && !package) PrintFatalError(R->getLoc(), "Checker '" + R->getName() + "' is neither named, nor in a package!"); if (isCheckerNamed(R)) { // Create a pseudo-group to hold this checker. std::string fullName = getCheckerFullName(R); GroupInfo &info = groupInfoByName[fullName]; info.Hidden = R->getValueAsBit("Hidden"); recordGroupMap[R] = &info; info.Checkers.insert(R); } else { recordGroupMap[package]->Checkers.insert(R); } Record *currR = isCheckerNamed(R) ? R : package; // Insert the checker and its parent packages into the subgroups set of // the corresponding parent package. while (DefInit *DI = dyn_cast(currR->getValueInit("ParentPackage"))) { Record *parentPackage = DI->getDef(); recordGroupMap[parentPackage]->SubGroups.insert(currR); currR = parentPackage; } // Insert the checker into the set of its group. if (DefInit *DI = dyn_cast(R->getValueInit("Group"))) recordGroupMap[DI->getDef()]->Checkers.insert(R); } // If a package is in group, add all its checkers and its sub-packages // checkers into the group. for (unsigned i = 0, e = packages.size(); i != e; ++i) if (DefInit *DI = dyn_cast(packages[i]->getValueInit("Group"))) addPackageToCheckerGroup(packages[i], DI->getDef(), recordGroupMap); typedef std::map SortedRecords; typedef llvm::DenseMap RecToSortIndex; SortedRecords sortedGroups; RecToSortIndex groupToSortIndex; OS << "\n#ifdef GET_GROUPS\n"; { for (unsigned i = 0, e = checkerGroups.size(); i != e; ++i) sortedGroups[checkerGroups[i]->getValueAsString("GroupName")] = checkerGroups[i]; unsigned sortIndex = 0; for (SortedRecords::iterator I = sortedGroups.begin(), E = sortedGroups.end(); I != E; ++I) { const Record *R = I->second; OS << "GROUP(" << "\""; OS.write_escaped(R->getValueAsString("GroupName")) << "\""; OS << ")\n"; groupToSortIndex[R] = sortIndex++; } } OS << "#endif // GET_GROUPS\n\n"; OS << "\n#ifdef GET_PACKAGES\n"; { SortedRecords sortedPackages; for (unsigned i = 0, e = packages.size(); i != e; ++i) sortedPackages[getPackageFullName(packages[i])] = packages[i]; for (SortedRecords::iterator I = sortedPackages.begin(), E = sortedPackages.end(); I != E; ++I) { const Record &R = *I->second; OS << "PACKAGE(" << "\""; OS.write_escaped(getPackageFullName(&R)) << "\", "; // Group index if (DefInit *DI = dyn_cast(R.getValueInit("Group"))) OS << groupToSortIndex[DI->getDef()] << ", "; else OS << "-1, "; // Hidden bit if (isHidden(R)) OS << "true"; else OS << "false"; OS << ")\n"; } } OS << "#endif // GET_PACKAGES\n\n"; OS << "\n#ifdef GET_CHECKERS\n"; for (unsigned i = 0, e = checkers.size(); i != e; ++i) { const Record &R = *checkers[i]; OS << "CHECKER(" << "\""; std::string name; if (isCheckerNamed(&R)) name = getCheckerFullName(&R); OS.write_escaped(name) << "\", "; OS << R.getName() << ", "; OS << getStringValue(R, "DescFile") << ", "; OS << "\""; OS.write_escaped(getStringValue(R, "HelpText")) << "\", "; // Group index if (DefInit *DI = dyn_cast(R.getValueInit("Group"))) OS << groupToSortIndex[DI->getDef()] << ", "; else OS << "-1, "; // Hidden bit if (isHidden(R)) OS << "true"; else OS << "false"; OS << ")\n"; } OS << "#endif // GET_CHECKERS\n\n"; unsigned index = 0; for (std::map::iterator I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) I->second.Index = index++; // Walk through the packages/groups/checkers emitting an array for each // set of checkers and an array for each set of subpackages. OS << "\n#ifdef GET_MEMBER_ARRAYS\n"; unsigned maxLen = 0; for (std::map::iterator I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) { maxLen = std::max(maxLen, (unsigned)I->first.size()); llvm::DenseSet &checkers = I->second.Checkers; if (!checkers.empty()) { OS << "static const short CheckerArray" << I->second.Index << "[] = { "; // Make the output order deterministic. std::map sorted; for (llvm::DenseSet::iterator I = checkers.begin(), E = checkers.end(); I != E; ++I) sorted[(*I)->getID()] = *I; for (std::map::iterator I = sorted.begin(), E = sorted.end(); I != E; ++I) OS << checkerRecIndexMap[I->second] << ", "; OS << "-1 };\n"; } llvm::DenseSet &subGroups = I->second.SubGroups; if (!subGroups.empty()) { OS << "static const short SubPackageArray" << I->second.Index << "[] = { "; // Make the output order deterministic. std::map sorted; for (llvm::DenseSet::iterator I = subGroups.begin(), E = subGroups.end(); I != E; ++I) sorted[(*I)->getID()] = *I; for (std::map::iterator I = sorted.begin(), E = sorted.end(); I != E; ++I) { OS << recordGroupMap[I->second]->Index << ", "; } OS << "-1 };\n"; } } OS << "#endif // GET_MEMBER_ARRAYS\n\n"; OS << "\n#ifdef GET_CHECKNAME_TABLE\n"; for (std::map::iterator I = groupInfoByName.begin(), E = groupInfoByName.end(); I != E; ++I) { // Group option string. OS << " { \""; OS.write_escaped(I->first) << "\"," << std::string(maxLen-I->first.size()+1, ' '); if (I->second.Checkers.empty()) OS << "0, "; else OS << "CheckerArray" << I->second.Index << ", "; // Subgroups. if (I->second.SubGroups.empty()) OS << "0, "; else OS << "SubPackageArray" << I->second.Index << ", "; OS << (I->second.Hidden ? "true" : "false"); OS << " },\n"; } OS << "#endif // GET_CHECKNAME_TABLE\n\n"; } } // end namespace clang