231 #include "clang/AST/ASTConsumer.h"
232 #include "clang/AST/ASTContext.h"
233 #include "clang/AST/RecursiveASTVisitor.h"
234 #include "clang/Basic/SourceManager.h"
235 #include "clang/Driver/Options.h"
236 #include "clang/Frontend/CompilerInstance.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;
258 using namespace clang::tooling;
259 using namespace llvm;
260 using namespace llvm::opt;
261 using namespace Modularize;
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 std::unique_ptr<OptTable> Opts(createDriverOptTable());
341 const unsigned IncludedFlagsBitmask = options::CC1Option;
342 unsigned MissingArgIndex, MissingArgCount;
343 SmallVector<const char *, 256> Argv;
344 for (
auto I = CLArgs.begin(), E = CLArgs.end(); I != E; ++I)
345 Argv.push_back(I->c_str());
346 InputArgList Args = Opts->ParseArgs(Argv, MissingArgIndex, MissingArgCount,
347 IncludedFlagsBitmask);
348 std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT);
349 return ModularizeUtilities::getCanonicalPath(Inputs.back());
355 static ArgumentsAdjuster
357 return [&Dependencies](
const CommandLineArguments &Args,
361 CommandLineArguments NewArgs(Args);
362 if (
int Count = FileDependents.size()) {
363 for (
int Index = 0; Index < Count; ++Index) {
364 NewArgs.push_back(
"-include");
365 std::string
File(std::string(
"\"") + FileDependents[Index] +
367 NewArgs.push_back(FileDependents[Index]);
371 NewArgs.insert(NewArgs.begin() + 1,
"-w");
373 if (std::find(NewArgs.begin(), NewArgs.end(),
"-x") == NewArgs.end()) {
374 NewArgs.insert(NewArgs.begin() + 2,
"-x");
375 NewArgs.insert(NewArgs.begin() + 3,
"c++");
391 Loc = SM.getExpansionLoc(Loc);
395 std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(Loc);
396 File = SM.getFileEntryForID(Decomposed.first);
400 Line = SM.getLineNumber(Decomposed.first, Decomposed.second);
401 Column = SM.getColumnNumber(Decomposed.first, Decomposed.second);
404 operator bool()
const {
return File !=
nullptr; }
442 static StringRef getKindName(EntryKind kind);
454 case EK_NumberOfKinds:
457 llvm_unreachable(
"invalid Entry kind");
493 CurHeaderContents[Loc.
File].push_back(HE);
496 SmallVector<Entry, 2> &Entries = (*this)[
Name];
497 for (
unsigned I = 0, N = Entries.size(); I != N; ++I) {
498 if (Entries[I].Kind == Kind && Entries[I].Loc == Loc)
504 Entries.push_back(E);
508 for (DenseMap<const FileEntry *, HeaderContents>::iterator
509 H = CurHeaderContents.begin(),
510 HEnd = CurHeaderContents.end();
513 std::sort(H->second.begin(), H->second.end());
516 DenseMap<const FileEntry *, HeaderContents>::iterator KnownH =
517 AllHeaderContents.find(H->first);
518 if (KnownH == AllHeaderContents.end()) {
520 AllHeaderContents.insert(*H);
525 if (H->second == KnownH->second)
529 std::set_symmetric_difference(
530 H->second.begin(), H->second.end(), KnownH->second.begin(),
531 KnownH->second.end(),
532 std::back_inserter(HeaderContentMismatches[H->first]));
535 CurHeaderContents.clear();
539 DenseMap<const FileEntry *, HeaderContents> CurHeaderContents;
540 DenseMap<const FileEntry *, HeaderContents> AllHeaderContents;
544 :
public RecursiveASTVisitor<CollectEntitiesVisitor> {
549 : SM(SM), Entities(Entities), PP(PP), PPTracker(PPTracker),
550 HadErrors(HadErrors) {}
582 SourceRange BlockRange = D->getSourceRange();
583 const char *LinkageLabel;
584 switch (D->getLanguage()) {
585 case LinkageSpecDecl::lang_c:
586 LinkageLabel =
"extern \"C\" {}";
588 case LinkageSpecDecl::lang_cxx:
589 LinkageLabel =
"extern \"C++\" {}";
592 if (!
PPTracker.checkForIncludesInBlock(
PP, BlockRange, LinkageLabel,
600 SourceRange BlockRange = D->getSourceRange();
601 std::string Label(
"namespace ");
602 Label += D->getName();
604 if (!
PPTracker.checkForIncludesInBlock(
PP, BlockRange, Label.c_str(),
613 if (!ND->getDeclContext()->isFileContext())
617 if (isa<NamespaceDecl>(ND) || isa<UsingDirectiveDecl>(ND) ||
618 isa<NamespaceAliasDecl>(ND) ||
619 isa<ClassTemplateSpecializationDecl>(ND) || isa<UsingDecl>(ND) ||
620 isa<ClassTemplateDecl>(ND) || isa<TemplateTypeParmDecl>(ND) ||
621 isa<TypeAliasTemplateDecl>(ND) || isa<UsingShadowDecl>(ND) ||
622 isa<FunctionDecl>(ND) || isa<FunctionTemplateDecl>(ND) ||
624 !cast<TagDecl>(ND)->isThisDeclarationADefinition()))
628 if (!ND->getDeclName())
633 llvm::raw_string_ostream OS(Name);
634 ND->printQualifiedName(OS);
659 Preprocessor &
PP, StringRef InFile,
int &HadErrors)
660 : Entities(Entities),
PPTracker(preprocessorTracker), PP(PP),
661 HadErrors(HadErrors) {
662 PPTracker.handlePreprocessorEntry(PP, InFile);
668 SourceManager &
SM = Ctx.getSourceManager();
672 .TraverseDecl(Ctx.getTranslationUnitDecl());
675 for (Preprocessor::macro_iterator M =
PP.macro_begin(),
676 MEnd =
PP.macro_end();
678 Location Loc(SM, M->second.getLatest()->getLocation());
686 Entities.mergeCurHeaderContents();
701 : Entities(Entities),
PPTracker(preprocessorTracker),
702 HadErrors(HadErrors) {}
705 std::unique_ptr<clang::ASTConsumer>
707 return llvm::make_unique<CollectEntitiesConsumer>(
708 Entities,
PPTracker, CI.getPreprocessor(), InFile, HadErrors);
722 : Entities(Entities),
PPTracker(preprocessorTracker),
723 HadErrors(HadErrors) {}
736 :
public RecursiveASTVisitor<CompileCheckVisitor> {
795 std::unique_ptr<clang::ASTConsumer>
797 return llvm::make_unique<CompileCheckConsumer>();
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;
891 CompileCheckFileErrors |= CompileCheckTool.run(&CompileCheckFactory);
892 if (CompileCheckFileErrors != 0) {
893 ModUtil->addUniqueProblemFile(CompileCheckFile);
897 ModUtil->addNoCompileErrorsFile(CompileCheckFile);
902 ClangTool Tool(*Compilations,
904 Tool.appendArgumentsAdjuster(
907 HadErrors |= Tool.run(&Factory);
910 typedef SmallVector<Location, 8> LocationArray;
911 typedef SmallVector<LocationArray, Entry::EK_NumberOfKinds> EntryBinArray;
912 EntryBinArray EntryBins;
916 EntryBins.push_back(Array);
920 for (EntityMap::iterator E = Entities.begin(), EEnd = Entities.end();
923 if (E->second.size() == 1)
926 for (EntryBinArray::iterator CI = EntryBins.begin(), CE = EntryBins.end();
932 for (
unsigned I = 0, N = E->second.size(); I != N; ++I) {
933 EntryBins[E->second[I].Kind].push_back(E->second[I].Loc);
937 for (EntryBinArray::iterator DI = EntryBins.begin(), DE = EntryBins.end();
938 DI != DE; ++DI, ++KindIndex) {
939 int ECount = DI->size();
943 LocationArray::iterator FI = DI->begin();
945 errs() <<
"error: " << kindName <<
" '" << E->first()
946 <<
"' defined at multiple locations:\n";
947 for (LocationArray::iterator FE = DI->end(); FI != FE; ++FI) {
948 errs() <<
" " << FI->File->getName() <<
":" << FI->Line <<
":"
949 << FI->Column <<
"\n";
950 ModUtil->addUniqueProblemFile(FI->File->getName());
958 if (PPTracker->reportInconsistentMacros(errs()))
963 if (PPTracker->reportInconsistentConditionals(errs()))
970 for (DenseMap<const FileEntry *, HeaderContents>::iterator
974 if (H->second.empty()) {
975 errs() <<
"internal error: phantom header content mismatch\n";
980 ModUtil->addUniqueProblemFile(H->first->getName());
981 errs() <<
"error: header '" << H->first->getName()
982 <<
"' has different contents depending on how it was included.\n";
983 for (
unsigned I = 0, N = H->second.size(); I != N; ++I) {
984 errs() <<
"note: '" << H->second[I].Name <<
"' in "
985 << H->second[I].Loc.File->getName() <<
" at "
986 << H->second[I].Loc.Line <<
":" << H->second[I].Loc.Column
987 <<
" not always provided\n";
992 ModUtil->displayProblemFiles();
993 ModUtil->displayGoodFiles();
994 ModUtil->displayCombinedFiles();
SourceLocation Loc
'#' location in the include directive
bool TraverseTemplateArguments(const TemplateArgument *Args, unsigned NumArgs)
friend bool operator>(const Location &X, const Location &Y)
void HandleTranslationUnit(ASTContext &Ctx) override
void HandleTranslationUnit(ASTContext &Ctx) override
static cl::opt< std::string > ProblemFilesList("problem-files-list", cl::init(""), cl::desc("List of files with compilation or modularization problems for"" assistant mode. This will be excluded."))
DenseMap< const FileEntry *, HeaderContents > HeaderContentMismatches
std::unique_ptr< clang::ASTConsumer > CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override
static cl::list< std::string > CC1Arguments(cl::ConsumeAfter, cl::desc("<arguments to be passed to front end>..."))
Common definitions for Modularize.
friend bool operator!=(const Location &X, const Location &Y)
static cl::opt< bool > CoverageCheckOnly("coverage-check-only", cl::init(false), cl::desc("Only do the coverage check."))
bool TraverseConstructorInitializer(CXXCtorInitializer *Init)
friend bool operator<=(const Location &X, const Location &Y)
llvm::SmallVector< std::string, 4 > DependentsVector
bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo)
static clang::FrontendPluginRegistry::Add< clang::tidy::ClangTidyPluginAction > X("clang-tidy","clang-tidy")
bool TraverseTemplateArguments(const TemplateArgument *Args, unsigned NumArgs)
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc)
bool TraverseTemplateName(TemplateName Template)
static cl::opt< bool > BlockCheckHeaderListOnly("block-check-header-list-only", cl::init(false), cl::desc("Only warn if #include directives are inside extern or namespace"" blocks if the included header is in the header list."))
static cl::opt< std::string > HeaderPrefix("prefix", cl::init(""), cl::desc("Prepend header file paths with this prefix."" If not specified,"" the files are considered to be relative to the header list file."))
CollectEntitiesAction(EntityMap &Entities, PreprocessorTracker &preprocessorTracker, int &HadErrors)
bool VisitNamedDecl(NamedDecl *ND)
static ArgumentsAdjuster getModularizeArgumentsAdjuster(DependencyMap &Dependencies)
static cl::list< std::string > ListFileNames(cl::Positional, cl::value_desc("list"), cl::desc("<list of one or more header list files>"), cl::CommaSeparated)
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C, Expr *Init)
ModularizeFrontendActionFactory(EntityMap &Entities, PreprocessorTracker &preprocessorTracker, int &HadErrors)
ModularizeUtilities class definition.
void mergeCurHeaderContents()
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS)
static std::string findInputFile(const CommandLineArguments &CLArgs)
friend bool operator<(const Location &X, const Location &Y)
void add(const std::string &Name, enum Entry::EntryKind Kind, Location Loc)
bool VisitNamespaceDecl(const NamespaceDecl *D)
bool TraverseStmt(Stmt *S)
bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo)
bool TraverseConstructorInitializer(CXXCtorInitializer *Init)
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."))
bool VisitNamespaceDecl(const NamespaceDecl *D)
std::unique_ptr< clang::ASTConsumer > CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override
bool createModuleMap(llvm::StringRef ModuleMapPath, llvm::ArrayRef< std::string > HeaderFileNames, llvm::ArrayRef< std::string > ProblemFileNames, DependencyMap &Dependencies, llvm::StringRef HeaderPrefix, llvm::StringRef RootModuleName)
Create the module map file.
PreprocessorTrackerImpl & PPTracker
bool TraverseStmt(Stmt *S)
int main(int Argc, const char **Argv)
bool VisitLinkageSpecDecl(LinkageSpecDecl *D)
bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS)
~CollectEntitiesConsumer() override
CompileCheckAction * create() override
bool TraverseTypeLoc(TypeLoc TL)
llvm::StringMap< DependentsVector > DependencyMap
static cl::opt< bool > NoCoverageCheck("no-coverage-check", cl::init(false), cl::desc("Don't do the coverage check."))
CollectEntitiesAction * create() override
static cl::list< std::string > IncludePaths("I", cl::desc("Include path for coverage check."), cl::ZeroOrMore, cl::value_desc("path"))
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C, Expr *Init)
static cl::opt< std::string > RootModule("root-module", cl::init(""), cl::desc("Specify the name of the root module."))
Macro expansions and preprocessor conditional consistency checker.
friend bool operator>=(const Location &X, const Location &Y)
friend bool operator==(const Location &X, const Location &Y)
bool VisitLinkageSpecDecl(LinkageSpecDecl *D)
bool TraverseTemplateName(TemplateName Template)
Location(SourceManager &SM, SourceLocation Loc)
bool TraverseType(QualType T)
std::vector< HeaderEntry > HeaderContents
bool VisitNamedDecl(NamedDecl *ND)
CollectEntitiesConsumer(EntityMap &Entities, PreprocessorTracker &preprocessorTracker, Preprocessor &PP, StringRef InFile, int &HadErrors)
Preprocessor tracker for modularize.
bool TraverseTypeLoc(TypeLoc TL)
bool TraverseType(QualType T)
bool TraverseTemplateArgument(const TemplateArgument &Arg)
static cl::opt< bool > DisplayFileLists("display-file-lists", cl::init(false), cl::desc("Display lists of good files (no compile errors), problem files,"" and a combined list with problem files preceded by a '#'."))
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS)
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc)
CollectEntitiesVisitor(SourceManager &SM, EntityMap &Entities, Preprocessor &PP, PreprocessorTracker &PPTracker, int &HadErrors)
CompileCheckFrontendActionFactory()
bool TraverseTemplateArgument(const TemplateArgument &Arg)
bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS)