1 //===--- extra/module-map-checker/CoverageChecker.cpp -------------------===// 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 file implements a class that validates a module map by checking that 11 // all headers in the corresponding directories are accounted for. 12 // 13 // This class uses a previously loaded module map object. 14 // Starting at the module map file directory, or just the include 15 // paths, if specified, it will collect the names of all the files it 16 // considers headers (no extension, .h, or .inc--if you need more, modify the 17 // ModularizeUtilities::isHeader function). 18 // It then compares the headers against those referenced 19 // in the module map, either explicitly named, or implicitly named via an 20 // umbrella directory or umbrella file, as parsed by the ModuleMap object. 21 // If headers are found which are not referenced or covered by an umbrella 22 // directory or file, warning messages will be produced, and the doChecks 23 // function will return an error code of 1. Other errors result in an error 24 // code of 2. If no problems are found, an error code of 0 is returned. 25 // 26 // Note that in the case of umbrella headers, this tool invokes the compiler 27 // to preprocess the file, and uses a callback to collect the header files 28 // included by the umbrella header or any of its nested includes. If any 29 // front end options are needed for these compiler invocations, these are 30 // to be passed in via the CommandLine parameter. 31 // 32 // Warning message have the form: 33 // 34 // warning: module.modulemap does not account for file: Level3A.h 35 // 36 // Note that for the case of the module map referencing a file that does 37 // not exist, the module map parser in Clang will (at the time of this 38 // writing) display an error message. 39 // 40 // Potential problems with this program: 41 // 42 // 1. Might need a better header matching mechanism, or extensions to the 43 // canonical file format used. 44 // 45 // 2. It might need to support additional header file extensions. 46 // 47 // Future directions: 48 // 49 // 1. Add an option to fix the problems found, writing a new module map. 50 // Include an extra option to add unaccounted-for headers as excluded. 51 // 52 //===----------------------------------------------------------------------===// 53 54 #include "ModularizeUtilities.h" 55 #include "clang/AST/ASTConsumer.h" 56 #include "CoverageChecker.h" 57 #include "clang/AST/ASTContext.h" 58 #include "clang/AST/RecursiveASTVisitor.h" 59 #include "clang/Basic/SourceManager.h" 60 #include "clang/Driver/Options.h" 61 #include "clang/Frontend/CompilerInstance.h" 62 #include "clang/Frontend/FrontendActions.h" 63 #include "clang/Lex/PPCallbacks.h" 64 #include "clang/Lex/Preprocessor.h" 65 #include "clang/Tooling/CompilationDatabase.h" 66 #include "clang/Tooling/Tooling.h" 67 #include "llvm/Option/Option.h" 68 #include "llvm/Support/CommandLine.h" 69 #include "llvm/Support/FileSystem.h" 70 #include "llvm/Support/Path.h" 71 #include "llvm/Support/raw_ostream.h" 72 73 using namespace Modularize; 74 using namespace clang; 75 using namespace clang::driver; 76 using namespace clang::driver::options; 77 using namespace clang::tooling; 78 namespace cl = llvm::cl; 79 namespace sys = llvm::sys; 80 81 // Preprocessor callbacks. 82 // We basically just collect include files. 83 class CoverageCheckerCallbacks : public PPCallbacks { 84 public: 85 CoverageCheckerCallbacks(CoverageChecker &Checker) : Checker(Checker) {} 86 ~CoverageCheckerCallbacks() override {} 87 88 // Include directive callback. 89 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, 90 StringRef FileName, bool IsAngled, 91 CharSourceRange FilenameRange, const FileEntry *File, 92 StringRef SearchPath, StringRef RelativePath, 93 const Module *Imported) override { 94 Checker.collectUmbrellaHeaderHeader(File->getName()); 95 } 96 97 private: 98 CoverageChecker &Checker; 99 }; 100 101 // Frontend action stuff: 102 103 // Consumer is responsible for setting up the callbacks. 104 class CoverageCheckerConsumer : public ASTConsumer { 105 public: 106 CoverageCheckerConsumer(CoverageChecker &Checker, Preprocessor &PP) { 107 // PP takes ownership. 108 PP.addPPCallbacks(llvm::make_unique<CoverageCheckerCallbacks>(Checker)); 109 } 110 }; 111 112 class CoverageCheckerAction : public SyntaxOnlyAction { 113 public: 114 CoverageCheckerAction(CoverageChecker &Checker) : Checker(Checker) {} 115 116 protected: 117 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, 118 StringRef InFile) override { 119 return llvm::make_unique<CoverageCheckerConsumer>(Checker, 120 CI.getPreprocessor()); 121 } 122 123 private: 124 CoverageChecker &Checker; 125 }; 126 127 class CoverageCheckerFrontendActionFactory : public FrontendActionFactory { 128 public: 129 CoverageCheckerFrontendActionFactory(CoverageChecker &Checker) 130 : Checker(Checker) {} 131 132 CoverageCheckerAction *create() override { 133 return new CoverageCheckerAction(Checker); 134 } 135 136 private: 137 CoverageChecker &Checker; 138 }; 139 140 // CoverageChecker class implementation. 141 142 // Constructor. 143 CoverageChecker::CoverageChecker(StringRef ModuleMapPath, 144 std::vector<std::string> &IncludePaths, 145 ArrayRef<std::string> CommandLine, 146 clang::ModuleMap *ModuleMap) 147 : ModuleMapPath(ModuleMapPath), IncludePaths(IncludePaths), 148 CommandLine(CommandLine), 149 ModMap(ModuleMap) {} 150 151 // Create instance of CoverageChecker, to simplify setting up 152 // subordinate objects. 153 CoverageChecker *CoverageChecker::createCoverageChecker( 154 StringRef ModuleMapPath, std::vector<std::string> &IncludePaths, 155 ArrayRef<std::string> CommandLine, clang::ModuleMap *ModuleMap) { 156 157 return new CoverageChecker(ModuleMapPath, IncludePaths, CommandLine, 158 ModuleMap); 159 } 160 161 // Do checks. 162 // Starting from the directory of the module.modulemap file, 163 // Find all header files, optionally looking only at files 164 // covered by the include path options, and compare against 165 // the headers referenced by the module.modulemap file. 166 // Display warnings for unaccounted-for header files. 167 // Returns error_code of 0 if there were no errors or warnings, 1 if there 168 // were warnings, 2 if any other problem, such as if a bad 169 // module map path argument was specified. 170 std::error_code CoverageChecker::doChecks() { 171 std::error_code returnValue; 172 173 // Collect the headers referenced in the modules. 174 collectModuleHeaders(); 175 176 // Collect the file system headers. 177 if (!collectFileSystemHeaders()) 178 return std::error_code(2, std::generic_category()); 179 180 // Do the checks. These save the problematic file names. 181 findUnaccountedForHeaders(); 182 183 // Check for warnings. 184 if (!UnaccountedForHeaders.empty()) 185 returnValue = std::error_code(1, std::generic_category()); 186 187 return returnValue; 188 } 189 190 // The following functions are called by doChecks. 191 192 // Collect module headers. 193 // Walks the modules and collects referenced headers into 194 // ModuleMapHeadersSet. 195 void CoverageChecker::collectModuleHeaders() { 196 for (ModuleMap::module_iterator I = ModMap->module_begin(), 197 E = ModMap->module_end(); 198 I != E; ++I) { 199 collectModuleHeaders(*I->second); 200 } 201 } 202 203 // Collect referenced headers from one module. 204 // Collects the headers referenced in the given module into 205 // ModuleMapHeadersSet. 206 // FIXME: Doesn't collect files from umbrella header. 207 bool CoverageChecker::collectModuleHeaders(const Module &Mod) { 208 209 if (const FileEntry *UmbrellaHeader = Mod.getUmbrellaHeader().Entry) { 210 // Collect umbrella header. 211 ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath( 212 UmbrellaHeader->getName())); 213 // Preprocess umbrella header and collect the headers it references. 214 if (!collectUmbrellaHeaderHeaders(UmbrellaHeader->getName())) 215 return false; 216 } 217 else if (const DirectoryEntry *UmbrellaDir = Mod.getUmbrellaDir().Entry) { 218 // Collect headers in umbrella directory. 219 if (!collectUmbrellaHeaders(UmbrellaDir->getName())) 220 return false; 221 } 222 223 for (auto &HeaderKind : Mod.Headers) 224 for (auto &Header : HeaderKind) 225 ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath( 226 Header.Entry->getName())); 227 228 for (Module::submodule_const_iterator MI = Mod.submodule_begin(), 229 MIEnd = Mod.submodule_end(); 230 MI != MIEnd; ++MI) 231 collectModuleHeaders(**MI); 232 233 return true; 234 } 235 236 // Collect headers from an umbrella directory. 237 bool CoverageChecker::collectUmbrellaHeaders(StringRef UmbrellaDirName) { 238 // Initialize directory name. 239 SmallString<256> Directory(ModuleMapDirectory); 240 if (UmbrellaDirName.size()) 241 sys::path::append(Directory, UmbrellaDirName); 242 if (Directory.size() == 0) 243 Directory = "."; 244 // Walk the directory. 245 std::error_code EC; 246 sys::fs::file_status Status; 247 for (sys::fs::directory_iterator I(Directory.str(), EC), E; I != E; 248 I.increment(EC)) { 249 if (EC) 250 return false; 251 std::string File(I->path()); 252 I->status(Status); 253 sys::fs::file_type Type = Status.type(); 254 // If the file is a directory, ignore the name and recurse. 255 if (Type == sys::fs::file_type::directory_file) { 256 if (!collectUmbrellaHeaders(File)) 257 return false; 258 continue; 259 } 260 // If the file does not have a common header extension, ignore it. 261 if (!ModularizeUtilities::isHeader(File)) 262 continue; 263 // Save header name. 264 ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(File)); 265 } 266 return true; 267 } 268 269 // Collect headers rferenced from an umbrella file. 270 bool 271 CoverageChecker::collectUmbrellaHeaderHeaders(StringRef UmbrellaHeaderName) { 272 273 SmallString<256> PathBuf(ModuleMapDirectory); 274 275 // If directory is empty, it's the current directory. 276 if (ModuleMapDirectory.length() == 0) 277 sys::fs::current_path(PathBuf); 278 279 // Create the compilation database. 280 std::unique_ptr<CompilationDatabase> Compilations; 281 Compilations.reset(new FixedCompilationDatabase(Twine(PathBuf), CommandLine)); 282 283 std::vector<std::string> HeaderPath; 284 HeaderPath.push_back(UmbrellaHeaderName); 285 286 // Create the tool and run the compilation. 287 ClangTool Tool(*Compilations, HeaderPath); 288 int HadErrors = Tool.run(new CoverageCheckerFrontendActionFactory(*this)); 289 290 // If we had errors, exit early. 291 return !HadErrors; 292 } 293 294 // Called from CoverageCheckerCallbacks to track a header included 295 // from an umbrella header. 296 void CoverageChecker::collectUmbrellaHeaderHeader(StringRef HeaderName) { 297 298 SmallString<256> PathBuf(ModuleMapDirectory); 299 // If directory is empty, it's the current directory. 300 if (ModuleMapDirectory.length() == 0) 301 sys::fs::current_path(PathBuf); 302 // HeaderName will have an absolute path, so if it's the module map 303 // directory, we remove it, also skipping trailing separator. 304 if (HeaderName.startswith(PathBuf)) 305 HeaderName = HeaderName.substr(PathBuf.size() + 1); 306 // Save header name. 307 ModuleMapHeadersSet.insert(ModularizeUtilities::getCanonicalPath(HeaderName)); 308 } 309 310 // Collect file system header files. 311 // This function scans the file system for header files, 312 // starting at the directory of the module.modulemap file, 313 // optionally filtering out all but the files covered by 314 // the include path options. 315 // Returns true if no errors. 316 bool CoverageChecker::collectFileSystemHeaders() { 317 318 // Get directory containing the module.modulemap file. 319 // Might be relative to current directory, absolute, or empty. 320 ModuleMapDirectory = ModularizeUtilities::getDirectoryFromPath(ModuleMapPath); 321 322 // If no include paths specified, we do the whole tree starting 323 // at the module.modulemap directory. 324 if (IncludePaths.size() == 0) { 325 if (!collectFileSystemHeaders(StringRef(""))) 326 return false; 327 } 328 else { 329 // Otherwise we only look at the sub-trees specified by the 330 // include paths. 331 for (std::vector<std::string>::const_iterator I = IncludePaths.begin(), 332 E = IncludePaths.end(); 333 I != E; ++I) { 334 if (!collectFileSystemHeaders(*I)) 335 return false; 336 } 337 } 338 339 // Sort it, because different file systems might order the file differently. 340 std::sort(FileSystemHeaders.begin(), FileSystemHeaders.end()); 341 342 return true; 343 } 344 345 // Collect file system header files from the given path. 346 // This function scans the file system for header files, 347 // starting at the given directory, which is assumed to be 348 // relative to the directory of the module.modulemap file. 349 // \returns True if no errors. 350 bool CoverageChecker::collectFileSystemHeaders(StringRef IncludePath) { 351 352 // Initialize directory name. 353 SmallString<256> Directory(ModuleMapDirectory); 354 if (IncludePath.size()) 355 sys::path::append(Directory, IncludePath); 356 if (Directory.size() == 0) 357 Directory = "."; 358 if (IncludePath.startswith("/") || IncludePath.startswith("\\") || 359 ((IncludePath.size() >= 2) && (IncludePath[1] == ':'))) { 360 llvm::errs() << "error: Include path \"" << IncludePath 361 << "\" is not relative to the module map file.\n"; 362 return false; 363 } 364 365 // Recursively walk the directory tree. 366 std::error_code EC; 367 sys::fs::file_status Status; 368 int Count = 0; 369 for (sys::fs::recursive_directory_iterator I(Directory.str(), EC), E; I != E; 370 I.increment(EC)) { 371 if (EC) 372 return false; 373 //std::string file(I->path()); 374 StringRef file(I->path()); 375 I->status(Status); 376 sys::fs::file_type type = Status.type(); 377 // If the file is a directory, ignore the name (but still recurses). 378 if (type == sys::fs::file_type::directory_file) 379 continue; 380 // Assume directories or files starting with '.' are private and not to 381 // be considered. 382 if ((file.find("\\.") != StringRef::npos) || 383 (file.find("/.") != StringRef::npos)) 384 continue; 385 // If the file does not have a common header extension, ignore it. 386 if (!ModularizeUtilities::isHeader(file)) 387 continue; 388 // Save header name. 389 FileSystemHeaders.push_back(ModularizeUtilities::getCanonicalPath(file)); 390 Count++; 391 } 392 if (Count == 0) { 393 llvm::errs() << "warning: No headers found in include path: \"" 394 << IncludePath << "\"\n"; 395 } 396 return true; 397 } 398 399 // Find headers unaccounted-for in module map. 400 // This function compares the list of collected header files 401 // against those referenced in the module map. Display 402 // warnings for unaccounted-for header files. 403 // Save unaccounted-for file list for possible. 404 // fixing action. 405 // FIXME: There probably needs to be some canonalization 406 // of file names so that header path can be correctly 407 // matched. Also, a map could be used for the headers 408 // referenced in the module, but 409 void CoverageChecker::findUnaccountedForHeaders() { 410 // Walk over file system headers. 411 for (std::vector<std::string>::const_iterator I = FileSystemHeaders.begin(), 412 E = FileSystemHeaders.end(); 413 I != E; ++I) { 414 // Look for header in module map. 415 if (ModuleMapHeadersSet.insert(*I).second) { 416 UnaccountedForHeaders.push_back(*I); 417 llvm::errs() << "warning: " << ModuleMapPath 418 << " does not account for file: " << *I << "\n"; 419 } 420 } 421 } 422