clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

CoverageChecker.cpp
Go to the documentation of this file.
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 }
CoverageCheckerCallbacks
Definition: CoverageChecker.cpp:83
Modularize::CoverageChecker::collectUmbrellaHeaders
bool collectUmbrellaHeaders(llvm::StringRef UmbrellaDirName)
Collect headers from an umbrella directory.
Definition: CoverageChecker.cpp:237
Modularize::ModularizeUtilities::getCanonicalPath
static std::string getCanonicalPath(llvm::StringRef FilePath)
Convert header path to canonical form.
Definition: ModularizeUtilities.cpp:454
Modularize::CoverageChecker::CoverageChecker
CoverageChecker(llvm::StringRef ModuleMapPath, std::vector< std::string > &IncludePaths, llvm::ArrayRef< std::string > CommandLine, clang::ModuleMap *ModuleMap)
Constructor.
Definition: CoverageChecker.cpp:143
CoverageCheckerCallbacks::CoverageCheckerCallbacks
CoverageCheckerCallbacks(CoverageChecker &Checker)
Definition: CoverageChecker.cpp:85
File
HeaderHandle File
Definition: PreprocessorTracker.cpp:526
CoverageCheckerAction::CreateASTConsumer
std::unique_ptr< ASTConsumer > CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override
Definition: CoverageChecker.cpp:117
Modularize::ModularizeUtilities::getDirectoryFromPath
static std::string getDirectoryFromPath(llvm::StringRef Path)
Get directory path component from file path.
Definition: ModularizeUtilities.cpp:484
Modularize::CoverageChecker::collectModuleHeaders
void collectModuleHeaders()
Collect module headers.
Definition: CoverageChecker.cpp:195
CoverageCheckerAction::CoverageCheckerAction
CoverageCheckerAction(CoverageChecker &Checker)
Definition: CoverageChecker.cpp:114
Modularize::CoverageChecker::collectUmbrellaHeaderHeader
void collectUmbrellaHeaderHeader(llvm::StringRef HeaderName)
Called from CoverageCheckerCallbacks to track a header included from an umbrella header.
Definition: CoverageChecker.cpp:296
CoverageCheckerFrontendActionFactory::CoverageCheckerFrontendActionFactory
CoverageCheckerFrontendActionFactory(CoverageChecker &Checker)
Definition: CoverageChecker.cpp:129
ModularizeUtilities.h
ModularizeUtilities class definition.
Modularize::CoverageChecker::findUnaccountedForHeaders
void findUnaccountedForHeaders()
Find headers unaccounted-for in module map.
Definition: CoverageChecker.cpp:409
CoverageCheckerCallbacks::~CoverageCheckerCallbacks
~CoverageCheckerCallbacks() override
Definition: CoverageChecker.cpp:86
CoverageCheckerFrontendActionFactory
Definition: CoverageChecker.cpp:127
Modularize::CoverageChecker::collectUmbrellaHeaderHeaders
bool collectUmbrellaHeaderHeaders(llvm::StringRef UmbrellaHeaderName)
Collect headers rferenced from an umbrella file.
Definition: CoverageChecker.cpp:271
Directory
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
ASTConsumer
CoverageChecker.h
Definitions for CoverageChecker.
ModuleMapPath
static cl::opt< std::string > ModuleMapPath("module-map-path", cl::init(""), cl::desc("Turn on module map output and specify output path or file name."" If no path is specified and if prefix option is specified,"" use prefix for file path."))
IsAngled
bool IsAngled
true if this was an include with angle brackets
Definition: IncludeOrderCheck.cpp:37
CoverageCheckerFrontendActionFactory::create
CoverageCheckerAction * create() override
Definition: CoverageChecker.cpp:132
SyntaxOnlyAction
CommandLine
std::string CommandLine
Definition: Modularize.cpp:336
PP
Preprocessor * PP
Definition: MacroParenthesesCheck.cpp:38
Modularize::CoverageChecker::doChecks
std::error_code doChecks()
Do checks.
Definition: CoverageChecker.cpp:170
IncludePaths
static cl::list< std::string > IncludePaths("I", cl::desc("Include path for coverage check."), cl::ZeroOrMore, cl::value_desc("path"))
Modularize::CoverageChecker::collectFileSystemHeaders
bool collectFileSystemHeaders()
Collect file system header files.
Definition: CoverageChecker.cpp:316
CoverageCheckerCallbacks::InclusionDirective
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, const Module *Imported) override
Definition: CoverageChecker.cpp:89
CoverageCheckerConsumer::CoverageCheckerConsumer
CoverageCheckerConsumer(CoverageChecker &Checker, Preprocessor &PP)
Definition: CoverageChecker.cpp:106
Modularize::CoverageChecker::createCoverageChecker
static CoverageChecker * createCoverageChecker(llvm::StringRef ModuleMapPath, std::vector< std::string > &IncludePaths, llvm::ArrayRef< std::string > CommandLine, clang::ModuleMap *ModuleMap)
Create instance of CoverageChecker.
Definition: CoverageChecker.cpp:153
CoverageCheckerConsumer
Definition: CoverageChecker.cpp:104
Modularize::CoverageChecker
Module map checker class.
Definition: CoverageChecker.h:39
FrontendActionFactory
Modularize::ModularizeUtilities::isHeader
static bool isHeader(llvm::StringRef FileName)
Check for header file extension.
Definition: ModularizeUtilities.cpp:468
CoverageCheckerAction
Definition: CoverageChecker.cpp:112