blob: 08c92fc90c0da24532ed3b84af6068feba3b82e3 [file] [log] [blame]
John Thompsond845bae2015-02-13 14:29:22 +00001//===--- extra/modularize/ModularizeUtilities.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 for loading and validating a module map or
11// header list by checking that all headers in the corresponding directories
12// are accounted for.
13//
14//===----------------------------------------------------------------------===//
15
John Thompson9cb79642015-02-18 16:14:32 +000016#include "clang/Basic/SourceManager.h"
17#include "clang/Driver/Options.h"
18#include "clang/Frontend/CompilerInstance.h"
19#include "clang/Frontend/FrontendActions.h"
John Thompson8eb8d932015-02-19 16:47:27 +000020#include "CoverageChecker.h"
Nick Lewyckyd07cfbd2015-02-20 07:05:56 +000021#include "llvm/ADT/SmallString.h"
22#include "llvm/Support/FileUtilities.h"
23#include "llvm/Support/MemoryBuffer.h"
24#include "llvm/Support/Path.h"
25#include "llvm/Support/raw_ostream.h"
John Thompson8eb8d932015-02-19 16:47:27 +000026#include "ModularizeUtilities.h"
John Thompsond845bae2015-02-13 14:29:22 +000027
John Thompson9cb79642015-02-18 16:14:32 +000028using namespace clang;
John Thompsond845bae2015-02-13 14:29:22 +000029using namespace llvm;
30using namespace Modularize;
31
Benjamin Kramere7103712015-03-23 12:49:15 +000032namespace {
John Thompson9cb79642015-02-18 16:14:32 +000033// Subclass TargetOptions so we can construct it inline with
34// the minimal option, the triple.
35class ModuleMapTargetOptions : public clang::TargetOptions {
36public:
37 ModuleMapTargetOptions() { Triple = llvm::sys::getDefaultTargetTriple(); }
38};
Benjamin Kramere7103712015-03-23 12:49:15 +000039} // namespace
John Thompson9cb79642015-02-18 16:14:32 +000040
John Thompsond845bae2015-02-13 14:29:22 +000041// ModularizeUtilities class implementation.
42
43// Constructor.
44ModularizeUtilities::ModularizeUtilities(std::vector<std::string> &InputPaths,
John Thompson4018c622015-07-10 00:37:25 +000045 llvm::StringRef Prefix,
46 llvm::StringRef ProblemFilesListPath)
John Thompson9cb79642015-02-18 16:14:32 +000047 : InputFilePaths(InputPaths),
48 HeaderPrefix(Prefix),
John Thompson4018c622015-07-10 00:37:25 +000049 ProblemFilesPath(ProblemFilesListPath),
John Thompson8eb8d932015-02-19 16:47:27 +000050 HasModuleMap(false),
John Thompson96f55512015-06-04 23:35:19 +000051 MissingHeaderCount(0),
John Thompson9cb79642015-02-18 16:14:32 +000052 // Init clang stuff needed for loading the module map and preprocessing.
53 LangOpts(new LangOptions()), DiagIDs(new DiagnosticIDs()),
54 DiagnosticOpts(new DiagnosticOptions()),
55 DC(llvm::errs(), DiagnosticOpts.get()),
56 Diagnostics(
57 new DiagnosticsEngine(DiagIDs, DiagnosticOpts.get(), &DC, false)),
58 TargetOpts(new ModuleMapTargetOptions()),
59 Target(TargetInfo::CreateTargetInfo(*Diagnostics, TargetOpts)),
60 FileMgr(new FileManager(FileSystemOpts)),
61 SourceMgr(new SourceManager(*Diagnostics, *FileMgr, false)),
62 HeaderSearchOpts(new HeaderSearchOptions()),
63 HeaderInfo(new HeaderSearch(HeaderSearchOpts, *SourceMgr, *Diagnostics,
64 *LangOpts, Target.get())) {
65}
John Thompsond845bae2015-02-13 14:29:22 +000066
67// Create instance of ModularizeUtilities, to simplify setting up
68// subordinate objects.
69ModularizeUtilities *ModularizeUtilities::createModularizeUtilities(
John Thompson4018c622015-07-10 00:37:25 +000070 std::vector<std::string> &InputPaths, llvm::StringRef Prefix,
71 llvm::StringRef ProblemFilesListPath) {
John Thompsond845bae2015-02-13 14:29:22 +000072
John Thompson4018c622015-07-10 00:37:25 +000073 return new ModularizeUtilities(InputPaths, Prefix, ProblemFilesListPath);
John Thompsond845bae2015-02-13 14:29:22 +000074}
75
76// Load all header lists and dependencies.
77std::error_code ModularizeUtilities::loadAllHeaderListsAndDependencies() {
78 typedef std::vector<std::string>::iterator Iter;
John Thompson9cb79642015-02-18 16:14:32 +000079 // For each input file.
John Thompsond845bae2015-02-13 14:29:22 +000080 for (Iter I = InputFilePaths.begin(), E = InputFilePaths.end(); I != E; ++I) {
John Thompson9cb79642015-02-18 16:14:32 +000081 llvm::StringRef InputPath = *I;
82 // If it's a module map.
83 if (InputPath.endswith(".modulemap")) {
84 // Load the module map.
85 if (std::error_code EC = loadModuleMap(InputPath))
86 return EC;
87 }
88 else {
89 // Else we assume it's a header list and load it.
90 if (std::error_code EC = loadSingleHeaderListsAndDependencies(InputPath)) {
91 errs() << "modularize: error: Unable to get header list '" << InputPath
92 << "': " << EC.message() << '\n';
93 return EC;
94 }
John Thompsond845bae2015-02-13 14:29:22 +000095 }
96 }
John Thompson4018c622015-07-10 00:37:25 +000097 // If we have a problem files list.
98 if (ProblemFilesPath.size() != 0) {
99 // Load problem files list.
100 if (std::error_code EC = loadProblemHeaderList(ProblemFilesPath)) {
101 errs() << "modularize: error: Unable to get problem header list '" << ProblemFilesPath
102 << "': " << EC.message() << '\n';
103 return EC;
104 }
105 }
John Thompsond845bae2015-02-13 14:29:22 +0000106 return std::error_code();
107}
John Thompson8eb8d932015-02-19 16:47:27 +0000108
109// Do coverage checks.
110// For each loaded module map, do header coverage check.
111// Starting from the directory of the module.map file,
112// Find all header files, optionally looking only at files
113// covered by the include path options, and compare against
114// the headers referenced by the module.map file.
115// Display warnings for unaccounted-for header files.
116// Returns 0 if there were no errors or warnings, 1 if there
117// were warnings, 2 if any other problem, such as a bad
118// module map path argument was specified.
119std::error_code ModularizeUtilities::doCoverageCheck(
120 std::vector<std::string> &IncludePaths,
121 llvm::ArrayRef<std::string> CommandLine) {
122 int ModuleMapCount = ModuleMaps.size();
123 int ModuleMapIndex;
124 std::error_code EC;
125 for (ModuleMapIndex = 0; ModuleMapIndex < ModuleMapCount; ++ModuleMapIndex) {
126 std::unique_ptr<clang::ModuleMap> &ModMap = ModuleMaps[ModuleMapIndex];
127 CoverageChecker *Checker = CoverageChecker::createCoverageChecker(
128 InputFilePaths[ModuleMapIndex], IncludePaths, CommandLine, ModMap.get());
129 std::error_code LocalEC = Checker->doChecks();
130 if (LocalEC.value() > 0)
131 EC = LocalEC;
132 }
133 return EC;
134}
135
John Thompsond845bae2015-02-13 14:29:22 +0000136// Load single header list and dependencies.
137std::error_code ModularizeUtilities::loadSingleHeaderListsAndDependencies(
138 llvm::StringRef InputPath) {
139
140 // By default, use the path component of the list file name.
141 SmallString<256> HeaderDirectory(InputPath);
142 llvm::sys::path::remove_filename(HeaderDirectory);
143 SmallString<256> CurrentDirectory;
144 llvm::sys::fs::current_path(CurrentDirectory);
145
146 // Get the prefix if we have one.
147 if (HeaderPrefix.size() != 0)
148 HeaderDirectory = HeaderPrefix;
149
150 // Read the header list file into a buffer.
151 ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer =
152 MemoryBuffer::getFile(InputPath);
153 if (std::error_code EC = listBuffer.getError())
154 return EC;
155
156 // Parse the header list into strings.
157 SmallVector<StringRef, 32> Strings;
158 listBuffer.get()->getBuffer().split(Strings, "\n", -1, false);
159
160 // Collect the header file names from the string list.
161 for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(),
162 E = Strings.end();
163 I != E; ++I) {
164 StringRef Line = I->trim();
165 // Ignore comments and empty lines.
166 if (Line.empty() || (Line[0] == '#'))
167 continue;
168 std::pair<StringRef, StringRef> TargetAndDependents = Line.split(':');
169 SmallString<256> HeaderFileName;
170 // Prepend header file name prefix if it's not absolute.
171 if (llvm::sys::path::is_absolute(TargetAndDependents.first))
172 llvm::sys::path::native(TargetAndDependents.first, HeaderFileName);
173 else {
174 if (HeaderDirectory.size() != 0)
175 HeaderFileName = HeaderDirectory;
176 else
177 HeaderFileName = CurrentDirectory;
178 llvm::sys::path::append(HeaderFileName, TargetAndDependents.first);
179 llvm::sys::path::native(HeaderFileName);
180 }
181 // Handle optional dependencies.
182 DependentsVector Dependents;
183 SmallVector<StringRef, 4> DependentsList;
184 TargetAndDependents.second.split(DependentsList, " ", -1, false);
185 int Count = DependentsList.size();
186 for (int Index = 0; Index < Count; ++Index) {
187 SmallString<256> Dependent;
188 if (llvm::sys::path::is_absolute(DependentsList[Index]))
189 Dependent = DependentsList[Index];
190 else {
191 if (HeaderDirectory.size() != 0)
192 Dependent = HeaderDirectory;
193 else
194 Dependent = CurrentDirectory;
195 llvm::sys::path::append(Dependent, DependentsList[Index]);
196 }
197 llvm::sys::path::native(Dependent);
John Thompson3dcb3932015-02-17 20:43:47 +0000198 Dependents.push_back(getCanonicalPath(Dependent.str()));
John Thompsond845bae2015-02-13 14:29:22 +0000199 }
John Thompson3dcb3932015-02-17 20:43:47 +0000200 // Get canonical form.
201 HeaderFileName = getCanonicalPath(HeaderFileName);
John Thompsond845bae2015-02-13 14:29:22 +0000202 // Save the resulting header file path and dependencies.
203 HeaderFileNames.push_back(HeaderFileName.str());
204 Dependencies[HeaderFileName.str()] = Dependents;
205 }
206 return std::error_code();
207}
John Thompson3dcb3932015-02-17 20:43:47 +0000208
John Thompson4018c622015-07-10 00:37:25 +0000209// Load problem header list.
210std::error_code ModularizeUtilities::loadProblemHeaderList(
211 llvm::StringRef InputPath) {
212
213 // By default, use the path component of the list file name.
214 SmallString<256> HeaderDirectory(InputPath);
215 llvm::sys::path::remove_filename(HeaderDirectory);
216 SmallString<256> CurrentDirectory;
217 llvm::sys::fs::current_path(CurrentDirectory);
218
219 // Get the prefix if we have one.
220 if (HeaderPrefix.size() != 0)
221 HeaderDirectory = HeaderPrefix;
222
223 // Read the header list file into a buffer.
224 ErrorOr<std::unique_ptr<MemoryBuffer>> listBuffer =
225 MemoryBuffer::getFile(InputPath);
226 if (std::error_code EC = listBuffer.getError())
227 return EC;
228
229 // Parse the header list into strings.
230 SmallVector<StringRef, 32> Strings;
231 listBuffer.get()->getBuffer().split(Strings, "\n", -1, false);
232
233 // Collect the header file names from the string list.
234 for (SmallVectorImpl<StringRef>::iterator I = Strings.begin(),
235 E = Strings.end();
236 I != E; ++I) {
237 StringRef Line = I->trim();
238 // Ignore comments and empty lines.
239 if (Line.empty() || (Line[0] == '#'))
240 continue;
241 SmallString<256> HeaderFileName;
242 // Prepend header file name prefix if it's not absolute.
243 if (llvm::sys::path::is_absolute(Line))
244 llvm::sys::path::native(Line, HeaderFileName);
245 else {
246 if (HeaderDirectory.size() != 0)
247 HeaderFileName = HeaderDirectory;
248 else
249 HeaderFileName = CurrentDirectory;
250 llvm::sys::path::append(HeaderFileName, Line);
251 llvm::sys::path::native(HeaderFileName);
252 }
253 // Get canonical form.
254 HeaderFileName = getCanonicalPath(HeaderFileName);
255 // Save the resulting header file path.
256 ProblemFileNames.push_back(HeaderFileName.str());
257 }
258 return std::error_code();
259}
260
John Thompson9cb79642015-02-18 16:14:32 +0000261// Load single module map and extract header file list.
262std::error_code ModularizeUtilities::loadModuleMap(
263 llvm::StringRef InputPath) {
264 // Get file entry for module.modulemap file.
265 const FileEntry *ModuleMapEntry =
266 SourceMgr->getFileManager().getFile(InputPath);
267
268 // return error if not found.
269 if (!ModuleMapEntry) {
270 llvm::errs() << "error: File \"" << InputPath << "\" not found.\n";
271 return std::error_code(1, std::generic_category());
272 }
273
274 // Because the module map parser uses a ForwardingDiagnosticConsumer,
275 // which doesn't forward the BeginSourceFile call, we do it explicitly here.
276 DC.BeginSourceFile(*LangOpts, nullptr);
277
278 // Figure out the home directory for the module map file.
279 const DirectoryEntry *Dir = ModuleMapEntry->getDir();
280 StringRef DirName(Dir->getName());
281 if (llvm::sys::path::filename(DirName) == "Modules") {
282 DirName = llvm::sys::path::parent_path(DirName);
283 if (DirName.endswith(".framework"))
284 Dir = FileMgr->getDirectory(DirName);
285 // FIXME: This assert can fail if there's a race between the above check
286 // and the removal of the directory.
287 assert(Dir && "parent must exist");
288 }
289
290 std::unique_ptr<ModuleMap> ModMap;
291 ModMap.reset(new ModuleMap(*SourceMgr, *Diagnostics, *LangOpts,
292 Target.get(), *HeaderInfo));
293
294 // Parse module.modulemap file into module map.
295 if (ModMap->parseModuleMapFile(ModuleMapEntry, false, Dir)) {
296 return std::error_code(1, std::generic_category());
297 }
298
299 // Do matching end call.
300 DC.EndSourceFile();
301
John Thompson96f55512015-06-04 23:35:19 +0000302 // Reset missing header count.
303 MissingHeaderCount = 0;
304
John Thompson9cb79642015-02-18 16:14:32 +0000305 if (!collectModuleMapHeaders(ModMap.get()))
306 return std::error_code(1, std::generic_category());
307
308 // Save module map.
309 ModuleMaps.push_back(std::move(ModMap));
310
John Thompson8eb8d932015-02-19 16:47:27 +0000311 // Indicate we are using module maps.
312 HasModuleMap = true;
313
John Thompson96f55512015-06-04 23:35:19 +0000314 // Return code of 1 for missing headers.
315 if (MissingHeaderCount)
316 return std::error_code(1, std::generic_category());
317
John Thompson9cb79642015-02-18 16:14:32 +0000318 return std::error_code();
319}
320
321// Collect module map headers.
322// Walks the modules and collects referenced headers into
John Thompson3c9fb522015-02-19 14:31:48 +0000323// HeaderFileNames.
John Thompson9cb79642015-02-18 16:14:32 +0000324bool ModularizeUtilities::collectModuleMapHeaders(clang::ModuleMap *ModMap) {
325 for (ModuleMap::module_iterator I = ModMap->module_begin(),
326 E = ModMap->module_end();
327 I != E; ++I) {
328 if (!collectModuleHeaders(*I->second))
329 return false;
330 }
331 return true;
332}
333
334// Collect referenced headers from one module.
335// Collects the headers referenced in the given module into
John Thompson3c9fb522015-02-19 14:31:48 +0000336// HeaderFileNames.
John Thompson9cb79642015-02-18 16:14:32 +0000337bool ModularizeUtilities::collectModuleHeaders(const Module &Mod) {
338
339 // Ignore explicit modules because they often have dependencies
340 // we can't know.
341 if (Mod.IsExplicit)
342 return true;
343
344 // Treat headers in umbrella directory as dependencies.
345 DependentsVector UmbrellaDependents;
346
347 // Recursively do submodules.
348 for (Module::submodule_const_iterator MI = Mod.submodule_begin(),
349 MIEnd = Mod.submodule_end();
350 MI != MIEnd; ++MI)
351 collectModuleHeaders(**MI);
352
Richard Smith9d5ae212015-05-16 03:10:31 +0000353 if (const FileEntry *UmbrellaHeader = Mod.getUmbrellaHeader().Entry) {
John Thompson9cb79642015-02-18 16:14:32 +0000354 std::string HeaderPath = getCanonicalPath(UmbrellaHeader->getName());
355 // Collect umbrella header.
356 HeaderFileNames.push_back(HeaderPath);
John Thompson9cb79642015-02-18 16:14:32 +0000357
358 // FUTURE: When needed, umbrella header header collection goes here.
359 }
Richard Smith9d5ae212015-05-16 03:10:31 +0000360 else if (const DirectoryEntry *UmbrellaDir = Mod.getUmbrellaDir().Entry) {
John Thompson9cb79642015-02-18 16:14:32 +0000361 // If there normal headers, assume these are umbrellas and skip collection.
362 if (Mod.Headers->size() == 0) {
363 // Collect headers in umbrella directory.
364 if (!collectUmbrellaHeaders(UmbrellaDir->getName(), UmbrellaDependents))
365 return false;
366 }
367 }
368
369 // We ignore HK_Private, HK_Textual, HK_PrivateTextual, and HK_Excluded,
370 // assuming they are marked as such either because of unsuitability for
371 // modules or because they are meant to be included by another header,
372 // and thus should be ignored by modularize.
373
374 int NormalHeaderCount = Mod.Headers[clang::Module::HK_Normal].size();
375
376 for (int Index = 0; Index < NormalHeaderCount; ++Index) {
377 DependentsVector NormalDependents;
378 // Collect normal header.
379 const clang::Module::Header &Header(
380 Mod.Headers[clang::Module::HK_Normal][Index]);
381 std::string HeaderPath = getCanonicalPath(Header.Entry->getName());
382 HeaderFileNames.push_back(HeaderPath);
383 }
384
John Thompson96f55512015-06-04 23:35:19 +0000385 int MissingCountThisModule = Mod.MissingHeaders.size();
386
387 for (int Index = 0; Index < MissingCountThisModule; ++Index) {
388 std::string MissingFile = Mod.MissingHeaders[Index].FileName;
389 SourceLocation Loc = Mod.MissingHeaders[Index].FileNameLoc;
390 errs() << Loc.printToString(*SourceMgr)
391 << ": error : Header not found: " << MissingFile << "\n";
392 }
393
394 MissingHeaderCount += MissingCountThisModule;
395
John Thompson9cb79642015-02-18 16:14:32 +0000396 return true;
397}
398
399// Collect headers from an umbrella directory.
400bool ModularizeUtilities::collectUmbrellaHeaders(StringRef UmbrellaDirName,
401 DependentsVector &Dependents) {
402 // Initialize directory name.
403 SmallString<256> Directory(UmbrellaDirName);
404 // Walk the directory.
405 std::error_code EC;
406 llvm::sys::fs::file_status Status;
407 for (llvm::sys::fs::directory_iterator I(Directory.str(), EC), E; I != E;
408 I.increment(EC)) {
409 if (EC)
410 return false;
411 std::string File(I->path());
412 I->status(Status);
413 llvm::sys::fs::file_type Type = Status.type();
414 // If the file is a directory, ignore the name and recurse.
415 if (Type == llvm::sys::fs::file_type::directory_file) {
416 if (!collectUmbrellaHeaders(File, Dependents))
417 return false;
418 continue;
419 }
420 // If the file does not have a common header extension, ignore it.
421 if (!isHeader(File))
422 continue;
423 // Save header name.
424 std::string HeaderPath = getCanonicalPath(File);
John Thompson9cb79642015-02-18 16:14:32 +0000425 Dependents.push_back(HeaderPath);
426 }
427 return true;
428}
John Thompson84ced5c2015-03-06 00:39:42 +0000429
430// Replace .. embedded in path for purposes of having
John Thompson4018c622015-07-10 00:37:25 +0000431// a canonical path.
Benjamin Kramere7103712015-03-23 12:49:15 +0000432static std::string replaceDotDot(StringRef Path) {
John Thompson4018c622015-07-10 00:37:25 +0000433 SmallString<128> Buffer;
434 llvm::sys::path::const_iterator B = llvm::sys::path::begin(Path),
435 E = llvm::sys::path::end(Path);
436 while (B != E) {
437 if (B->compare(".") == 0) {
438 }
439 else if (B->compare("..") == 0)
440 llvm::sys::path::remove_filename(Buffer);
441 else
442 llvm::sys::path::append(Buffer, *B);
443 ++B;
444 }
445 if (Path.endswith("/") || Path.endswith("\\"))
446 Buffer.append(1, Path.back());
447 return Buffer.c_str();
448}
John Thompson9cb79642015-02-18 16:14:32 +0000449
John Thompson3dcb3932015-02-17 20:43:47 +0000450// Convert header path to canonical form.
451// The canonical form is basically just use forward slashes, and remove "./".
452// \param FilePath The file path, relative to the module map directory.
453// \returns The file path in canonical form.
454std::string ModularizeUtilities::getCanonicalPath(StringRef FilePath) {
John Thompson84ced5c2015-03-06 00:39:42 +0000455 std::string Tmp(replaceDotDot(FilePath));
John Thompson3dcb3932015-02-17 20:43:47 +0000456 std::replace(Tmp.begin(), Tmp.end(), '\\', '/');
457 StringRef Tmp2(Tmp);
458 if (Tmp2.startswith("./"))
459 Tmp = Tmp2.substr(2);
460 return Tmp;
461}
John Thompson9cb79642015-02-18 16:14:32 +0000462
463// Check for header file extension.
464// If the file extension is .h, .inc, or missing, it's
465// assumed to be a header.
466// \param FileName The file name. Must not be a directory.
467// \returns true if it has a header extension or no extension.
468bool ModularizeUtilities::isHeader(StringRef FileName) {
469 StringRef Extension = llvm::sys::path::extension(FileName);
470 if (Extension.size() == 0)
471 return false;
472 if (Extension.equals_lower(".h"))
473 return true;
474 if (Extension.equals_lower(".inc"))
475 return true;
476 return false;
477}
John Thompson8eb8d932015-02-19 16:47:27 +0000478
479// Get directory path component from file path.
480// \returns the component of the given path, which will be
481// relative if the given path is relative, absolute if the
482// given path is absolute, or "." if the path has no leading
483// path component.
484std::string ModularizeUtilities::getDirectoryFromPath(StringRef Path) {
485 SmallString<256> Directory(Path);
486 sys::path::remove_filename(Directory);
487 if (Directory.size() == 0)
488 return ".";
489 return Directory.str();
490}
John Thompson4018c622015-07-10 00:37:25 +0000491
492// Add unique problem file.
493// Also standardizes the path.
494void ModularizeUtilities::addUniqueProblemFile(std::string FilePath) {
495 FilePath = getCanonicalPath(FilePath);
496 // Don't add if already present.
497 for(auto &TestFilePath : ProblemFileNames) {
498 if (TestFilePath == FilePath)
499 return;
500 }
501 ProblemFileNames.push_back(FilePath);
502}
503
504// Add file with no compile errors.
505// Also standardizes the path.
506void ModularizeUtilities::addNoCompileErrorsFile(std::string FilePath) {
507 FilePath = getCanonicalPath(FilePath);
508 GoodFileNames.push_back(FilePath);
509}
510
511// List problem files.
512void ModularizeUtilities::displayProblemFiles() {
513 errs() << "\nThese are the files with possible errors:\n\n";
514 for (auto &ProblemFile : ProblemFileNames) {
515 errs() << ProblemFile << "\n";
516 }
517}
518
519// List files with no problems.
520void ModularizeUtilities::displayGoodFiles() {
521 errs() << "\nThese are the files with no detected errors:\n\n";
522 for (auto &GoodFile : HeaderFileNames) {
523 bool Good = true;
524 for (auto &ProblemFile : ProblemFileNames) {
525 if (ProblemFile == GoodFile) {
526 Good = false;
527 break;
528 }
529 }
530 if (Good)
531 errs() << GoodFile << "\n";
532 }
533}
534
535// List files with problem files commented out.
536void ModularizeUtilities::displayCombinedFiles() {
537 errs() <<
538 "\nThese are the combined files, with problem files preceded by #:\n\n";
539 for (auto &File : HeaderFileNames) {
540 bool Good = true;
541 for (auto &ProblemFile : ProblemFileNames) {
542 if (ProblemFile == File) {
543 Good = false;
544 break;
545 }
546 }
547 errs() << (Good ? "" : "#") << File << "\n";
548 }
549}