230 #include "clang/AST/ASTConsumer.h"
231 #include "clang/AST/ASTContext.h"
232 #include "clang/AST/RecursiveASTVisitor.h"
233 #include "clang/Basic/SourceManager.h"
234 #include "clang/Driver/Options.h"
235 #include "clang/Frontend/CompilerInstance.h"
236 #include "clang/Frontend/FrontendAction.h"
237 #include "clang/Frontend/FrontendActions.h"
238 #include "clang/Lex/Preprocessor.h"
239 #include "clang/Tooling/CompilationDatabase.h"
240 #include "clang/Tooling/Tooling.h"
241 #include "llvm/Option/Arg.h"
242 #include "llvm/Option/ArgList.h"
243 #include "llvm/Option/OptTable.h"
244 #include "llvm/Option/Option.h"
245 #include "llvm/Support/CommandLine.h"
246 #include "llvm/Support/FileSystem.h"
247 #include "llvm/Support/MemoryBuffer.h"
248 #include "llvm/Support/Path.h"
255 using namespace clang;
256 using namespace clang::driver;
257 using namespace clang::driver::options;
259 using namespace llvm;
260 using namespace llvm::opt;
264 static cl::list<std::string>
266 cl::desc(
"<list of one or more header list files>"),
270 static cl::list<std::string>
272 cl::desc(
"<arguments to be passed to front end>..."));
276 "prefix", cl::init(
""),
278 "Prepend header file paths with this prefix."
280 " the files are considered to be relative to the header list file."));
285 "module-map-path", cl::init(
""),
286 cl::desc(
"Turn on module map output and specify output path or file name."
287 " If no path is specified and if prefix option is specified,"
288 " use prefix for file path."));
293 "problem-files-list", cl::init(
""),
295 "List of files with compilation or modularization problems for"
296 " assistant mode. This will be excluded."));
299 static cl::opt<std::string>
301 cl::desc(
"Specify the name of the root module."));
309 cl::desc(
"Only warn if #include directives are inside extern or namespace"
310 " blocks if the included header is in the header list."));
313 static cl::list<std::string>
314 IncludePaths(
"I", cl::desc(
"Include path for coverage check."),
315 cl::ZeroOrMore, cl::value_desc(
"path"));
320 cl::desc(
"Don't do the coverage check."));
325 cl::desc(
"Only do the coverage check."));
330 cl::desc(
"Display lists of good files (no compile errors), problem files,"
331 " and a combined list with problem files preceded by a '#'."));
340 const unsigned IncludedFlagsBitmask = options::CC1Option;
341 unsigned MissingArgIndex, MissingArgCount;
342 SmallVector<const char *, 256> Argv;
343 for (
auto I = CLArgs.begin(),
E = CLArgs.end(); I !=
E; ++I)
344 Argv.push_back(I->c_str());
345 InputArgList Args = getDriverOptTable().ParseArgs(
346 Argv, MissingArgIndex, MissingArgCount, IncludedFlagsBitmask);
347 std::vector<std::string>
Inputs = Args.getAllArgValues(OPT_INPUT);
354 static ArgumentsAdjuster
356 return [&Dependencies](
const CommandLineArguments &Args,
360 CommandLineArguments NewArgs(Args);
361 if (
int Count = FileDependents.size()) {
363 NewArgs.push_back(
"-include");
364 std::string File(std::string(
"\"") + FileDependents[
Index] +
366 NewArgs.push_back(FileDependents[
Index]);
370 NewArgs.insert(NewArgs.begin() + 1,
"-w");
372 if (!llvm::is_contained(NewArgs,
"-x")) {
373 NewArgs.insert(NewArgs.begin() + 2,
"-x");
374 NewArgs.insert(NewArgs.begin() + 3,
"c++");
390 Loc = SM.getExpansionLoc(
Loc);
394 std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(
Loc);
395 File = SM.getFileEntryForID(Decomposed.first);
399 Line = SM.getLineNumber(Decomposed.first, Decomposed.second);
400 Column = SM.getColumnNumber(Decomposed.first, Decomposed.second);
403 operator bool()
const {
return File !=
nullptr; }
414 if (
X.File != Y.
File)
415 return X.File < Y.
File;
416 if (
X.Line != Y.
Line)
417 return X.Line < Y.
Line;
441 static StringRef getKindName(EntryKind kind);
453 case EK_NumberOfKinds:
456 llvm_unreachable(
"invalid Entry kind");
464 return X.Loc == Y.
Loc &&
X.Name == Y.
Name;
492 CurHeaderContents[
Loc.File].push_back(HE);
495 SmallVector<Entry, 2> &Entries = (*this)[
Name];
496 for (
unsigned I = 0, N = Entries.size(); I != N; ++I) {
503 Entries.push_back(
E);
507 for (DenseMap<const FileEntry *, HeaderContents>::iterator
508 H = CurHeaderContents.begin(),
509 HEnd = CurHeaderContents.end();
512 llvm::sort(H->second);
515 DenseMap<const FileEntry *, HeaderContents>::iterator KnownH =
516 AllHeaderContents.find(H->first);
517 if (KnownH == AllHeaderContents.end()) {
519 AllHeaderContents.insert(*H);
524 if (H->second == KnownH->second)
528 std::set_symmetric_difference(
529 H->second.begin(), H->second.end(), KnownH->second.begin(),
530 KnownH->second.end(),
531 std::back_inserter(HeaderContentMismatches[H->first]));
534 CurHeaderContents.clear();
538 DenseMap<const FileEntry *, HeaderContents> CurHeaderContents;
539 DenseMap<const FileEntry *, HeaderContents> AllHeaderContents;
543 :
public RecursiveASTVisitor<CollectEntitiesVisitor> {
548 : SM(SM), Entities(Entities),
PP(
PP), PPTracker(PPTracker),
549 HadErrors(HadErrors) {}
581 SourceRange BlockRange = D->getSourceRange();
582 const char *LinkageLabel;
583 switch (D->getLanguage()) {
584 case LinkageSpecDecl::lang_c:
585 LinkageLabel =
"extern \"C\" {}";
587 case LinkageSpecDecl::lang_cxx:
588 LinkageLabel =
"extern \"C++\" {}";
591 if (!PPTracker.checkForIncludesInBlock(
PP, BlockRange, LinkageLabel,
599 SourceRange BlockRange = D->getSourceRange();
600 std::string Label(
"namespace ");
601 Label += D->getName();
603 if (!PPTracker.checkForIncludesInBlock(
PP, BlockRange, Label.c_str(),
612 if (!ND->getDeclContext()->isFileContext())
616 if (isa<NamespaceDecl>(ND) || isa<UsingDirectiveDecl>(ND) ||
617 isa<NamespaceAliasDecl>(ND) ||
618 isa<ClassTemplateSpecializationDecl>(ND) || isa<UsingDecl>(ND) ||
619 isa<ClassTemplateDecl>(ND) || isa<TemplateTypeParmDecl>(ND) ||
620 isa<TypeAliasTemplateDecl>(ND) || isa<UsingShadowDecl>(ND) ||
621 isa<FunctionDecl>(ND) || isa<FunctionTemplateDecl>(ND) ||
623 !cast<TagDecl>(ND)->isThisDeclarationADefinition()))
627 if (!ND->getDeclName())
632 llvm::raw_string_ostream
OS(
Name);
633 ND->printQualifiedName(
OS);
658 Preprocessor &
PP, StringRef InFile,
int &HadErrors)
659 : Entities(Entities), PPTracker(preprocessorTracker),
PP(
PP),
660 HadErrors(HadErrors) {
661 PPTracker.handlePreprocessorEntry(
PP, InFile);
667 SourceManager &SM =
Ctx.getSourceManager();
671 .TraverseDecl(
Ctx.getTranslationUnitDecl());
674 for (Preprocessor::macro_iterator M =
PP.macro_begin(),
675 MEnd =
PP.macro_end();
677 Location Loc(SM, M->second.getLatest()->getLocation());
685 Entities.mergeCurHeaderContents();
700 : Entities(Entities), PPTracker(preprocessorTracker),
701 HadErrors(HadErrors) {}
704 std::unique_ptr<clang::ASTConsumer>
706 return std::make_unique<CollectEntitiesConsumer>(
707 Entities, PPTracker,
CI.getPreprocessor(), InFile, HadErrors);
721 : Entities(Entities), PPTracker(preprocessorTracker),
722 HadErrors(HadErrors) {}
724 std::unique_ptr<FrontendAction>
create()
override {
725 return std::make_unique<CollectEntitiesAction>(Entities, PPTracker,
736 :
public RecursiveASTVisitor<CompileCheckVisitor> {
795 std::unique_ptr<clang::ASTConsumer>
797 return std::make_unique<CompileCheckConsumer>();
805 std::unique_ptr<FrontendAction>
create()
override {
806 return std::make_unique<CompileCheckAction>();
810 int main(
int Argc,
const char **Argv) {
817 for (
int ArgIndex = 1; ArgIndex < Argc; ArgIndex++) {
823 cl::ParseCommandLineOptions(Argc, Argv,
"modularize.\n");
827 cl::PrintHelpMessage();
831 std::unique_ptr<ModularizeUtilities> ModUtil;
835 ModularizeUtilities::createModularizeUtilities(
839 if (ModUtil->loadAllHeaderListsAndDependencies())
845 ModUtil->ProblemFileNames,
863 SmallString<256> PathBuf;
864 sys::fs::current_path(PathBuf);
865 std::unique_ptr<CompilationDatabase> Compilations;
867 new FixedCompilationDatabase(Twine(PathBuf),
CC1Arguments));
870 std::unique_ptr<PreprocessorTracker> PPTracker(
871 PreprocessorTracker::create(ModUtil->HeaderFileNames,
883 for (
auto &CompileCheckFile : ModUtil->HeaderFileNames) {
884 llvm::SmallVector<std::string, 32> CompileCheckFileArray;
885 CompileCheckFileArray.push_back(CompileCheckFile);
886 ClangTool CompileCheckTool(*Compilations, CompileCheckFileArray);
887 CompileCheckTool.appendArgumentsAdjuster(
889 int CompileCheckFileErrors = 0;
892 CompileCheckFileErrors |= CompileCheckTool.run(&CompileCheckFactory);
893 if (CompileCheckFileErrors != 0) {
894 ModUtil->addUniqueProblemFile(CompileCheckFile);
898 ModUtil->addNoCompileErrorsFile(CompileCheckFile);
903 ClangTool Tool(*Compilations,
905 Tool.appendArgumentsAdjuster(
908 HadErrors |= Tool.run(&Factory);
911 typedef SmallVector<Location, 8> LocationArray;
912 typedef SmallVector<LocationArray, Entry::EK_NumberOfKinds> EntryBinArray;
913 EntryBinArray EntryBins;
917 EntryBins.push_back(Array);
921 for (EntityMap::iterator
E = Entities.begin(), EEnd = Entities.end();
924 if (
E->second.size() == 1)
927 for (EntryBinArray::iterator
CI = EntryBins.begin(),
CE = EntryBins.end();
933 for (
unsigned I = 0, N =
E->second.size(); I != N; ++I) {
934 EntryBins[
E->second[I].Kind].push_back(
E->second[I].Loc);
938 for (EntryBinArray::iterator DI = EntryBins.begin(), DE = EntryBins.end();
939 DI != DE; ++DI, ++KindIndex) {
940 int ECount = DI->size();
944 LocationArray::iterator FI = DI->begin();
946 errs() <<
"error: " << kindName <<
" '" <<
E->first()
947 <<
"' defined at multiple locations:\n";
948 for (LocationArray::iterator FE = DI->end(); FI != FE; ++FI) {
949 errs() <<
" " << FI->File->getName() <<
":" << FI->Line <<
":"
950 << FI->Column <<
"\n";
951 ModUtil->addUniqueProblemFile(std::string(FI->File->getName()));
959 if (PPTracker->reportInconsistentMacros(errs()))
964 if (PPTracker->reportInconsistentConditionals(errs()))
971 for (DenseMap<const FileEntry *, HeaderContents>::iterator
975 if (H->second.empty()) {
976 errs() <<
"internal error: phantom header content mismatch\n";
981 ModUtil->addUniqueProblemFile(std::string(H->first->getName()));
982 errs() <<
"error: header '" << H->first->getName()
983 <<
"' has different contents depending on how it was included.\n";
984 for (
unsigned I = 0, N = H->second.size(); I != N; ++I) {
985 errs() <<
"note: '" << H->second[I].Name <<
"' in "
986 << H->second[I].Loc.File->getName() <<
" at "
987 << H->second[I].Loc.Line <<
":" << H->second[I].Loc.Column
988 <<
" not always provided\n";
993 ModUtil->displayProblemFiles();
994 ModUtil->displayGoodFiles();
995 ModUtil->displayCombinedFiles();