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 (CommandLineArguments::const_iterator I = CLArgs.begin(),
347 Argv.push_back(I->c_str());
348 InputArgList Args = Opts->ParseArgs(Argv, MissingArgIndex, MissingArgCount,
349 IncludedFlagsBitmask);
350 std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT);
351 return ModularizeUtilities::getCanonicalPath(Inputs.back());
357 static ArgumentsAdjuster
359 return [&Dependencies](
const CommandLineArguments &Args,
363 CommandLineArguments NewArgs(Args);
364 if (
int Count = FileDependents.size()) {
365 for (
int Index = 0; Index < Count; ++Index) {
366 NewArgs.push_back(
"-include");
367 std::string
File(std::string(
"\"") + FileDependents[Index] +
369 NewArgs.push_back(FileDependents[Index]);
373 NewArgs.insert(NewArgs.begin() + 1,
"-w");
375 if (std::find(NewArgs.begin(), NewArgs.end(),
"-x") == NewArgs.end()) {
376 NewArgs.insert(NewArgs.begin() + 2,
"-x");
377 NewArgs.insert(NewArgs.begin() + 3,
"c++");
393 Loc = SM.getExpansionLoc(Loc);
397 std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(Loc);
398 File = SM.getFileEntryForID(Decomposed.first);
402 Line = SM.getLineNumber(Decomposed.first, Decomposed.second);
403 Column = SM.getColumnNumber(Decomposed.first, Decomposed.second);
406 operator bool()
const {
return File !=
nullptr; }
444 static StringRef getKindName(EntryKind kind);
456 case EK_NumberOfKinds:
459 llvm_unreachable(
"invalid Entry kind");
495 CurHeaderContents[Loc.
File].push_back(HE);
498 SmallVector<Entry, 2> &Entries = (*this)[
Name];
499 for (
unsigned I = 0, N = Entries.size(); I != N; ++I) {
500 if (Entries[I].Kind == Kind && Entries[I].Loc == Loc)
506 Entries.push_back(E);
510 for (DenseMap<const FileEntry *, HeaderContents>::iterator
511 H = CurHeaderContents.begin(),
512 HEnd = CurHeaderContents.end();
515 std::sort(H->second.begin(), H->second.end());
518 DenseMap<const FileEntry *, HeaderContents>::iterator KnownH =
519 AllHeaderContents.find(H->first);
520 if (KnownH == AllHeaderContents.end()) {
522 AllHeaderContents.insert(*H);
527 if (H->second == KnownH->second)
531 std::set_symmetric_difference(
532 H->second.begin(), H->second.end(), KnownH->second.begin(),
533 KnownH->second.end(),
534 std::back_inserter(HeaderContentMismatches[H->first]));
537 CurHeaderContents.clear();
541 DenseMap<const FileEntry *, HeaderContents> CurHeaderContents;
542 DenseMap<const FileEntry *, HeaderContents> AllHeaderContents;
546 :
public RecursiveASTVisitor<CollectEntitiesVisitor> {
551 : SM(SM), Entities(Entities), PP(PP), PPTracker(PPTracker),
552 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 llvm::make_unique<CollectEntitiesConsumer>(
707 Entities,
PPTracker, CI.getPreprocessor(), InFile, HadErrors);
721 : Entities(Entities),
PPTracker(preprocessorTracker),
722 HadErrors(HadErrors) {}
735 :
public RecursiveASTVisitor<CompileCheckVisitor> {
791 std::unique_ptr<clang::ASTConsumer>
793 return llvm::make_unique<CompileCheckConsumer>();
806 int main(
int Argc,
const char **Argv) {
813 for (
int ArgIndex = 1; ArgIndex < Argc; ArgIndex++) {
819 cl::ParseCommandLineOptions(Argc, Argv,
"modularize.\n");
823 cl::PrintHelpMessage();
827 std::unique_ptr<ModularizeUtilities> ModUtil;
831 ModularizeUtilities::createModularizeUtilities(
835 if (ModUtil->loadAllHeaderListsAndDependencies())
841 ModUtil->ProblemFileNames,
859 SmallString<256> PathBuf;
860 sys::fs::current_path(PathBuf);
861 std::unique_ptr<CompilationDatabase> Compilations;
863 new FixedCompilationDatabase(Twine(PathBuf),
CC1Arguments));
866 std::unique_ptr<PreprocessorTracker>
PPTracker(
867 PreprocessorTracker::create(ModUtil->HeaderFileNames,
879 for (
auto &CompileCheckFile : ModUtil->HeaderFileNames) {
880 llvm::SmallVector<std::string, 32> CompileCheckFileArray;
881 CompileCheckFileArray.push_back(CompileCheckFile);
882 ClangTool CompileCheckTool(*Compilations, CompileCheckFileArray);
883 CompileCheckTool.appendArgumentsAdjuster(
885 int CompileCheckFileErrors = 0;
887 CompileCheckFileErrors |= CompileCheckTool.run(&CompileCheckFactory);
888 if (CompileCheckFileErrors != 0) {
889 ModUtil->addUniqueProblemFile(CompileCheckFile);
893 ModUtil->addNoCompileErrorsFile(CompileCheckFile);
898 ClangTool Tool(*Compilations,
900 Tool.appendArgumentsAdjuster(
903 HadErrors |= Tool.run(&Factory);
906 typedef SmallVector<Location, 8> LocationArray;
907 typedef SmallVector<LocationArray, Entry::EK_NumberOfKinds> EntryBinArray;
908 EntryBinArray EntryBins;
912 EntryBins.push_back(Array);
916 for (EntityMap::iterator E = Entities.begin(), EEnd = Entities.end();
919 if (E->second.size() == 1)
922 for (EntryBinArray::iterator CI = EntryBins.begin(), CE = EntryBins.end();
928 for (
unsigned I = 0, N = E->second.size(); I != N; ++I) {
929 EntryBins[E->second[I].Kind].push_back(E->second[I].Loc);
933 for (EntryBinArray::iterator DI = EntryBins.begin(), DE = EntryBins.end();
934 DI != DE; ++DI, ++KindIndex) {
935 int ECount = DI->size();
939 LocationArray::iterator FI = DI->begin();
941 errs() <<
"error: " << kindName <<
" '" << E->first()
942 <<
"' defined at multiple locations:\n";
943 for (LocationArray::iterator FE = DI->end(); FI != FE; ++FI) {
944 errs() <<
" " << FI->File->getName() <<
":" << FI->Line <<
":"
945 << FI->Column <<
"\n";
946 ModUtil->addUniqueProblemFile(FI->File->getName());
954 if (PPTracker->reportInconsistentMacros(errs()))
959 if (PPTracker->reportInconsistentConditionals(errs()))
966 for (DenseMap<const FileEntry *, HeaderContents>::iterator
970 if (H->second.empty()) {
971 errs() <<
"internal error: phantom header content mismatch\n";
976 ModUtil->addUniqueProblemFile(H->first->getName());
977 errs() <<
"error: header '" << H->first->getName()
978 <<
"' has different contents depending on how it was included.\n";
979 for (
unsigned I = 0, N = H->second.size(); I != N; ++I) {
980 errs() <<
"note: '" << H->second[I].Name <<
"' in "
981 << H->second[I].Loc.File->getName() <<
" at "
982 << H->second[I].Loc.Line <<
":" << H->second[I].Loc.Column
983 <<
" not always provided\n";
988 ModUtil->displayProblemFiles();
989 ModUtil->displayGoodFiles();
990 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)
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 TraverseLambdaCapture(LambdaCapture C)
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"))
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 TraverseLambdaCapture(LambdaCapture C)
bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS)