978 lines · cpp
1//===- extra/modularize/Modularize.cpp - Check modularized headers --------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Introduction10//11// This file implements a tool that checks whether a set of headers provides12// the consistent definitions required to use modules. It can also check an13// existing module map for full coverage of the headers in a directory tree.14//15// For example, in examining headers, it detects whether the same entity16// (say, a NULL macro or size_t typedef) is defined in multiple headers17// or whether a header produces different definitions under18// different circumstances. These conditions cause modules built from the19// headers to behave poorly, and should be fixed before introducing a module20// map.21//22// Modularize takes as input either one or more module maps (by default,23// "module.modulemap") or one or more text files containing lists of headers24// to check.25//26// In the case of a module map, the module map must be well-formed in27// terms of syntax. Modularize will extract the header file names28// from the map. Only normal headers are checked, assuming headers29// marked "private", "textual", or "exclude" are not to be checked30// as a top-level include, assuming they either are included by31// other headers which are checked, or they are not suitable for32// modules.33//34// In the case of a file list, the list is a newline-separated list of headers35// to check with respect to each other.36// Lines beginning with '#' and empty lines are ignored.37// Header file names followed by a colon and other space-separated38// file names will include those extra files as dependencies.39// The file names can be relative or full paths, but must be on the40// same line.41//42// Modularize also accepts regular clang front-end arguments.43//44// Usage: modularize [(modularize options)]45// [(include-files_list)|(module map)]+ [(front-end-options) ...]46//47// Options:48// -prefix=(optional header path prefix)49// Note that unless a "-prefix (header path)" option is specified,50// non-absolute file paths in the header list file will be relative51// to the header list file directory. Use -prefix to specify a52// different directory.53// -module-map-path=(module map)54// Skip the checks, and instead act as a module.modulemap generation55// assistant, generating a module map file based on the header list.56// An optional "-root-module=(rootName)" argument can specify a root57// module to be created in the generated module.modulemap file. Note58// that you will likely need to edit this file to suit the needs of59// your headers.60// -problem-files-list=(problem files list file name)61// For use only with module map assistant. Input list of files that62// have problems with respect to modules. These will still be63// included in the generated module map, but will be marked as64// "excluded" headers.65// -root-module=(root module name)66// Specifies a root module to be created in the generated67// module.modulemap file.68// -block-check-header-list-only69// Only warn if #include directives are inside extern or namespace70// blocks if the included header is in the header list.71// -no-coverage-check72// Don't do the coverage check.73// -coverage-check-only74// Only do the coverage check.75// -display-file-lists76// Display lists of good files (no compile errors), problem files,77// and a combined list with problem files preceded by a '#'.78// This can be used to quickly determine which files have problems.79// The latter combined list might be useful in starting to modularize80// a set of headers. You can start with a full list of headers,81// use -display-file-lists option, and then use the combined list as82// your intermediate list, uncommenting-out headers as you fix them.83//84// Note that by default, the modularize assumes .h files contain C++ source.85// If your .h files in the file list contain another language, you should86// append an appropriate -x option to your command line, i.e.: -x c87//88// Modularization Issue Checks89//90// In the process of checking headers for modularization issues, modularize91// will do normal parsing, reporting normal errors and warnings,92// but will also report special error messages like the following:93//94// error: '(symbol)' defined at multiple locations:95// (file):(row):(column)96// (file):(row):(column)97//98// error: header '(file)' has different contents depending on how it was99// included100//101// The latter might be followed by messages like the following:102//103// note: '(symbol)' in (file) at (row):(column) not always provided104//105// Checks will also be performed for macro expansions, defined(macro)106// expressions, and preprocessor conditional directives that evaluate107// inconsistently, and can produce error messages like the following:108//109// (...)/SubHeader.h:11:5:110// #if SYMBOL == 1111// ^112// error: Macro instance 'SYMBOL' has different values in this header,113// depending on how it was included.114// 'SYMBOL' expanded to: '1' with respect to these inclusion paths:115// (...)/Header1.h116// (...)/SubHeader.h117// (...)/SubHeader.h:3:9:118// #define SYMBOL 1119// ^120// Macro defined here.121// 'SYMBOL' expanded to: '2' with respect to these inclusion paths:122// (...)/Header2.h123// (...)/SubHeader.h124// (...)/SubHeader.h:7:9:125// #define SYMBOL 2126// ^127// Macro defined here.128//129// Checks will also be performed for '#include' directives that are130// nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,131// and can produce error message like the following:132//133// IncludeInExtern.h:2:3134// #include "Empty.h"135// ^136// error: Include directive within extern "C" {}.137// IncludeInExtern.h:1:1138// extern "C" {139// ^140// The "extern "C" {}" block is here.141//142// See PreprocessorTracker.cpp for additional details.143//144// Module Map Coverage Check145//146// The coverage check uses the Clang ModuleMap class to read and parse the147// module map file. Starting at the module map file directory, or just the148// include paths, if specified, it will collect the names of all the files it149// considers headers (no extension, .h, or .inc--if you need more, modify the150// isHeader function). It then compares the headers against those referenced151// in the module map, either explicitly named, or implicitly named via an152// umbrella directory or umbrella file, as parsed by the ModuleMap object.153// If headers are found which are not referenced or covered by an umbrella154// directory or file, warning messages will be produced, and this program155// will return an error code of 1. Other errors result in an error code of 2.156// If no problems are found, an error code of 0 is returned.157//158// Note that in the case of umbrella headers, this tool invokes the compiler159// to preprocess the file, and uses a callback to collect the header files160// included by the umbrella header or any of its nested includes. If any161// front end options are needed for these compiler invocations, these162// can be included on the command line after the module map file argument.163//164// Warning message have the form:165//166// warning: module.modulemap does not account for file: Level3A.h167//168// Note that for the case of the module map referencing a file that does169// not exist, the module map parser in Clang will (at the time of this170// writing) display an error message.171//172// Module Map Assistant - Module Map Generation173//174// Modularize also has an option ("-module-map-path=module.modulemap") that will175// skip the checks, and instead act as a module.modulemap generation assistant,176// generating a module map file based on the header list. An optional177// "-root-module=(rootName)" argument can specify a root module to be178// created in the generated module.modulemap file. Note that you will likely179// need to edit this file to suit the needs of your headers.180//181// An example command line for generating a module.modulemap file:182//183// modularize -module-map-path=module.modulemap -root-module=myroot \184// headerlist.txt185//186// Note that if the headers in the header list have partial paths, sub-modules187// will be created for the subdirectories involved, assuming that the188// subdirectories contain headers to be grouped into a module, but still with189// individual modules for the headers in the subdirectory.190//191// See the ModuleAssistant.cpp file comments for additional details about the192// implementation of the assistant mode.193//194// Future directions:195//196// Basically, we want to add new checks for whatever we can check with respect197// to checking headers for module'ability.198//199// Some ideas:200//201// 1. Omit duplicate "not always provided" messages202//203// 2. Add options to disable any of the checks, in case204// there is some problem with them, or the messages get too verbose.205//206// 3. Try to figure out the preprocessor conditional directives that207// contribute to problems and tie them to the inconsistent definitions.208//209// 4. There are some legitimate uses of preprocessor macros that210// modularize will flag as errors, such as repeatedly #include'ing211// a file and using interleaving defined/undefined macros212// to change declarations in the included file. Is there a way213// to address this? Maybe have modularize accept a list of macros214// to ignore. Otherwise you can just exclude the file, after checking215// for legitimate errors.216//217// 5. What else?218//219// General clean-up and refactoring:220//221// 1. The Location class seems to be something that we might222// want to design to be applicable to a wider range of tools, and stick it223// somewhere into Tooling/ in mainline224//225//===----------------------------------------------------------------------===//226 227#include "Modularize.h"228#include "ModularizeUtilities.h"229#include "PreprocessorTracker.h"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/Frontend/CompilerInstance.h"235#include "clang/Frontend/FrontendAction.h"236#include "clang/Frontend/FrontendActions.h"237#include "clang/Lex/Preprocessor.h"238#include "clang/Options/Options.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"249#include <algorithm>250#include <iterator>251#include <map>252#include <string>253#include <vector>254 255using namespace clang;256using namespace clang::driver;257using namespace clang::options;258using namespace clang::tooling;259using namespace llvm;260using namespace llvm::opt;261using namespace Modularize;262 263// Option to specify a file name for a list of header files to check.264static cl::list<std::string>265 ListFileNames(cl::Positional, cl::value_desc("list"),266 cl::desc("<list of one or more header list files>"),267 cl::CommaSeparated);268 269// Collect all other arguments, which will be passed to the front end.270static cl::list<std::string>271 CC1Arguments(cl::ConsumeAfter,272 cl::desc("<arguments to be passed to front end>..."));273 274// Option to specify a prefix to be prepended to the header names.275static cl::opt<std::string> HeaderPrefix(276 "prefix", cl::init(""),277 cl::desc(278 "Prepend header file paths with this prefix."279 " If not specified,"280 " the files are considered to be relative to the header list file."));281 282// Option for assistant mode, telling modularize to output a module map283// based on the headers list, and where to put it.284static cl::opt<std::string> ModuleMapPath(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."));289 290// Option to specify list of problem files for assistant.291// This will cause assistant to exclude these files.292static cl::opt<std::string> ProblemFilesList(293 "problem-files-list", cl::init(""),294 cl::desc(295 "List of files with compilation or modularization problems for"296 " assistant mode. This will be excluded."));297 298// Option for assistant mode, telling modularize the name of the root module.299static cl::opt<std::string>300RootModule("root-module", cl::init(""),301 cl::desc("Specify the name of the root module."));302 303// Option for limiting the #include-inside-extern-or-namespace-block304// check to only those headers explicitly listed in the header list.305// This is a work-around for private includes that purposefully get306// included inside blocks.307static cl::opt<bool>308BlockCheckHeaderListOnly("block-check-header-list-only", cl::init(false),309cl::desc("Only warn if #include directives are inside extern or namespace"310 " blocks if the included header is in the header list."));311 312// Option for include paths for coverage check.313static cl::list<std::string>314 IncludePaths("I", cl::desc("Include path for coverage check."),315 cl::value_desc("path"));316 317// Option for disabling the coverage check.318static cl::opt<bool> NoCoverageCheck("no-coverage-check",319 cl::desc("Don't do the coverage check."));320 321// Option for just doing the coverage check.322static cl::opt<bool>323CoverageCheckOnly("coverage-check-only", cl::init(false),324cl::desc("Only do the coverage check."));325 326// Option for displaying lists of good, bad, and mixed files.327static cl::opt<bool>328DisplayFileLists("display-file-lists", cl::init(false),329cl::desc("Display lists of good files (no compile errors), problem files,"330 " and a combined list with problem files preceded by a '#'."));331 332// Save the program name for error messages.333const char *Argv0;334// Save the command line for comments.335std::string CommandLine;336 337// Helper function for finding the input file in an arguments list.338static std::string findInputFile(const CommandLineArguments &CLArgs) {339 llvm::opt::Visibility VisibilityMask(options::CC1Option);340 unsigned MissingArgIndex, MissingArgCount;341 SmallVector<const char *, 256> Argv;342 for (const std::string &CLArg : CLArgs)343 Argv.push_back(CLArg.c_str());344 InputArgList Args = getDriverOptTable().ParseArgs(345 Argv, MissingArgIndex, MissingArgCount, VisibilityMask);346 std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT);347 return ModularizeUtilities::getCanonicalPath(Inputs.back());348}349 350// This arguments adjuster inserts "-include (file)" arguments for header351// dependencies. It also inserts a "-w" option and a "-x c++",352// if no other "-x" option is present.353static ArgumentsAdjuster354getModularizeArgumentsAdjuster(DependencyMap &Dependencies) {355 return [&Dependencies](const CommandLineArguments &Args,356 StringRef /*unused*/) {357 std::string InputFile = findInputFile(Args);358 DependentsVector &FileDependents = Dependencies[InputFile];359 CommandLineArguments NewArgs(Args);360 for (const std::string &Dep : FileDependents) {361 NewArgs.push_back("-include");362 NewArgs.push_back(Dep);363 }364 // Ignore warnings. (Insert after "clang_tool" at beginning.)365 NewArgs.insert(NewArgs.begin() + 1, "-w");366 // Since we are compiling .h files, assume C++ unless given a -x option.367 if (!llvm::is_contained(NewArgs, "-x")) {368 NewArgs.insert(NewArgs.begin() + 2, "-x");369 NewArgs.insert(NewArgs.begin() + 3, "c++");370 }371 return NewArgs;372 };373}374 375// FIXME: The Location class seems to be something that we might376// want to design to be applicable to a wider range of tools, and stick it377// somewhere into Tooling/ in mainline378struct Location {379 OptionalFileEntryRef File;380 unsigned Line = 0, Column = 0;381 382 Location() = default;383 384 Location(SourceManager &SM, SourceLocation Loc) {385 Loc = SM.getExpansionLoc(Loc);386 if (Loc.isInvalid())387 return;388 389 std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(Loc);390 File = SM.getFileEntryRefForID(Decomposed.first);391 if (!File)392 return;393 394 Line = SM.getLineNumber(Decomposed.first, Decomposed.second);395 Column = SM.getColumnNumber(Decomposed.first, Decomposed.second);396 }397 398 explicit operator bool() const { return File != nullptr; }399 400 friend bool operator==(const Location &X, const Location &Y) {401 return X.File == Y.File && X.Line == Y.Line && X.Column == Y.Column;402 }403 404 friend bool operator!=(const Location &X, const Location &Y) {405 return !(X == Y);406 }407 408 friend bool operator<(const Location &X, const Location &Y) {409 return std::tie(X.File, X.Line, X.Column) <410 std::tie(Y.File, Y.Line, Y.Column);411 }412 friend bool operator>(const Location &X, const Location &Y) { return Y < X; }413 friend bool operator<=(const Location &X, const Location &Y) {414 return !(Y < X);415 }416 friend bool operator>=(const Location &X, const Location &Y) {417 return !(X < Y);418 }419};420 421struct Entry {422 enum EntryKind {423 EK_Tag,424 EK_Value,425 EK_Macro,426 427 EK_NumberOfKinds428 } Kind;429 430 Location Loc;431 432 StringRef getKindName() { return getKindName(Kind); }433 static StringRef getKindName(EntryKind kind);434};435 436// Return a string representing the given kind.437StringRef Entry::getKindName(Entry::EntryKind kind) {438 switch (kind) {439 case EK_Tag:440 return "tag";441 case EK_Value:442 return "value";443 case EK_Macro:444 return "macro";445 case EK_NumberOfKinds:446 break;447 }448 llvm_unreachable("invalid Entry kind");449}450 451struct HeaderEntry {452 std::string Name;453 Location Loc;454 455 friend bool operator==(const HeaderEntry &X, const HeaderEntry &Y) {456 return X.Loc == Y.Loc && X.Name == Y.Name;457 }458 friend bool operator!=(const HeaderEntry &X, const HeaderEntry &Y) {459 return !(X == Y);460 }461 friend bool operator<(const HeaderEntry &X, const HeaderEntry &Y) {462 return std::tie(X.Loc, X.Name) < std::tie(Y.Loc, Y.Name);463 }464 friend bool operator>(const HeaderEntry &X, const HeaderEntry &Y) {465 return Y < X;466 }467 friend bool operator<=(const HeaderEntry &X, const HeaderEntry &Y) {468 return !(Y < X);469 }470 friend bool operator>=(const HeaderEntry &X, const HeaderEntry &Y) {471 return !(X < Y);472 }473};474 475typedef std::vector<HeaderEntry> HeaderContents;476 477class EntityMap : public std::map<std::string, SmallVector<Entry, 2>> {478public:479 DenseMap<FileEntryRef, HeaderContents> HeaderContentMismatches;480 481 void add(const std::string &Name, enum Entry::EntryKind Kind, Location Loc) {482 // Record this entity in its header.483 HeaderEntry HE = { Name, Loc };484 CurHeaderContents[*Loc.File].push_back(HE);485 486 // Check whether we've seen this entry before.487 SmallVector<Entry, 2> &Entries = (*this)[Name];488 for (unsigned I = 0, N = Entries.size(); I != N; ++I) {489 if (Entries[I].Kind == Kind && Entries[I].Loc == Loc)490 return;491 }492 493 // We have not seen this entry before; record it.494 Entry E = { Kind, Loc };495 Entries.push_back(E);496 }497 498 void mergeCurHeaderContents() {499 for (auto H = CurHeaderContents.begin(), HEnd = CurHeaderContents.end();500 H != HEnd; ++H) {501 // Sort contents.502 llvm::sort(H->second);503 504 // Record this header and its contents if we haven't seen it before.505 auto [KnownH, Inserted] = AllHeaderContents.insert(*H);506 if (Inserted)507 continue;508 509 // If the header contents are the same, we're done.510 if (H->second == KnownH->second)511 continue;512 513 // Determine what changed.514 std::set_symmetric_difference(515 H->second.begin(), H->second.end(), KnownH->second.begin(),516 KnownH->second.end(),517 std::back_inserter(HeaderContentMismatches[H->first]));518 }519 520 CurHeaderContents.clear();521 }522 523private:524 DenseMap<FileEntryRef, HeaderContents> CurHeaderContents;525 DenseMap<FileEntryRef, HeaderContents> AllHeaderContents;526};527 528class CollectEntitiesVisitor529 : public RecursiveASTVisitor<CollectEntitiesVisitor> {530public:531 CollectEntitiesVisitor(SourceManager &SM, EntityMap &Entities,532 Preprocessor &PP, PreprocessorTracker &PPTracker,533 int &HadErrors)534 : SM(SM), Entities(Entities), PP(PP), PPTracker(PPTracker),535 HadErrors(HadErrors) {}536 537 bool TraverseStmt(Stmt *S) { return true; }538 bool TraverseType(QualType T) { return true; }539 bool TraverseTypeLoc(TypeLoc TL) { return true; }540 bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }541 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {542 return true;543 }544 bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {545 return true;546 }547 bool TraverseTemplateName(TemplateName Template) { return true; }548 bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }549 bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {550 return true;551 }552 bool TraverseTemplateArguments(ArrayRef<TemplateArgument>) { return true; }553 bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }554 bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,555 Expr *Init) {556 return true;557 }558 559 // Check 'extern "*" {}' block for #include directives.560 bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {561 // Bail if not a block.562 if (!D->hasBraces())563 return true;564 SourceRange BlockRange = D->getSourceRange();565 const char *LinkageLabel;566 switch (D->getLanguage()) {567 case LinkageSpecLanguageIDs::C:568 LinkageLabel = "extern \"C\" {}";569 break;570 case LinkageSpecLanguageIDs::CXX:571 LinkageLabel = "extern \"C++\" {}";572 break;573 }574 if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, LinkageLabel,575 errs()))576 HadErrors = 1;577 return true;578 }579 580 // Check 'namespace (name) {}' block for #include directives.581 bool VisitNamespaceDecl(const NamespaceDecl *D) {582 SourceRange BlockRange = D->getSourceRange();583 std::string Label("namespace ");584 Label += D->getName();585 Label += " {}";586 if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, Label.c_str(),587 errs()))588 HadErrors = 1;589 return true;590 }591 592 // Collect definition entities.593 bool VisitNamedDecl(NamedDecl *ND) {594 // We only care about file-context variables.595 if (!ND->getDeclContext()->isFileContext())596 return true;597 598 // Skip declarations that tend to be properly multiply-declared.599 if (isa<NamespaceDecl>(ND) || isa<UsingDirectiveDecl>(ND) ||600 isa<NamespaceAliasDecl>(ND) ||601 isa<ClassTemplateSpecializationDecl>(ND) || isa<UsingDecl>(ND) ||602 isa<ClassTemplateDecl>(ND) || isa<TemplateTypeParmDecl>(ND) ||603 isa<TypeAliasTemplateDecl>(ND) || isa<UsingShadowDecl>(ND) ||604 isa<FunctionDecl>(ND) || isa<FunctionTemplateDecl>(ND) ||605 (isa<TagDecl>(ND) &&606 !cast<TagDecl>(ND)->isThisDeclarationADefinition()))607 return true;608 609 // Skip anonymous declarations.610 if (!ND->getDeclName())611 return true;612 613 // Get the qualified name.614 std::string Name;615 llvm::raw_string_ostream OS(Name);616 ND->printQualifiedName(OS);617 if (Name.empty())618 return true;619 620 Location Loc(SM, ND->getLocation());621 if (!Loc)622 return true;623 624 Entities.add(Name, isa<TagDecl>(ND) ? Entry::EK_Tag : Entry::EK_Value, Loc);625 return true;626 }627 628private:629 SourceManager &SM;630 EntityMap &Entities;631 Preprocessor &PP;632 PreprocessorTracker &PPTracker;633 int &HadErrors;634};635 636class CollectEntitiesConsumer : public ASTConsumer {637public:638 CollectEntitiesConsumer(EntityMap &Entities,639 PreprocessorTracker &preprocessorTracker,640 Preprocessor &PP, StringRef InFile, int &HadErrors)641 : Entities(Entities), PPTracker(preprocessorTracker), PP(PP),642 HadErrors(HadErrors) {643 PPTracker.handlePreprocessorEntry(PP, InFile);644 }645 646 ~CollectEntitiesConsumer() override { PPTracker.handlePreprocessorExit(); }647 648 void HandleTranslationUnit(ASTContext &Ctx) override {649 SourceManager &SM = Ctx.getSourceManager();650 651 // Collect declared entities.652 CollectEntitiesVisitor(SM, Entities, PP, PPTracker, HadErrors)653 .TraverseDecl(Ctx.getTranslationUnitDecl());654 655 // Collect macro definitions.656 for (Preprocessor::macro_iterator M = PP.macro_begin(),657 MEnd = PP.macro_end();658 M != MEnd; ++M) {659 Location Loc(SM, M->second.getLatest()->getLocation());660 if (!Loc)661 continue;662 663 Entities.add(M->first->getName().str(), Entry::EK_Macro, Loc);664 }665 666 // Merge header contents.667 Entities.mergeCurHeaderContents();668 }669 670private:671 EntityMap &Entities;672 PreprocessorTracker &PPTracker;673 Preprocessor &PP;674 int &HadErrors;675};676 677class CollectEntitiesAction : public SyntaxOnlyAction {678public:679 CollectEntitiesAction(EntityMap &Entities,680 PreprocessorTracker &preprocessorTracker,681 int &HadErrors)682 : Entities(Entities), PPTracker(preprocessorTracker),683 HadErrors(HadErrors) {}684 685protected:686 std::unique_ptr<clang::ASTConsumer>687 CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {688 return std::make_unique<CollectEntitiesConsumer>(689 Entities, PPTracker, CI.getPreprocessor(), InFile, HadErrors);690 }691 692private:693 EntityMap &Entities;694 PreprocessorTracker &PPTracker;695 int &HadErrors;696};697 698class ModularizeFrontendActionFactory : public FrontendActionFactory {699public:700 ModularizeFrontendActionFactory(EntityMap &Entities,701 PreprocessorTracker &preprocessorTracker,702 int &HadErrors)703 : Entities(Entities), PPTracker(preprocessorTracker),704 HadErrors(HadErrors) {}705 706 std::unique_ptr<FrontendAction> create() override {707 return std::make_unique<CollectEntitiesAction>(Entities, PPTracker,708 HadErrors);709 }710 711private:712 EntityMap &Entities;713 PreprocessorTracker &PPTracker;714 int &HadErrors;715};716 717class CompileCheckVisitor718 : public RecursiveASTVisitor<CompileCheckVisitor> {719public:720 CompileCheckVisitor() {}721 722 bool TraverseStmt(Stmt *S) { return true; }723 bool TraverseType(QualType T) { return true; }724 bool TraverseTypeLoc(TypeLoc TL) { return true; }725 bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }726 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {727 return true;728 }729 bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {730 return true;731 }732 bool TraverseTemplateName(TemplateName Template) { return true; }733 bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }734 bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {735 return true;736 }737 bool TraverseTemplateArguments(ArrayRef<TemplateArgument>) { return true; }738 bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }739 bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,740 Expr *Init) {741 return true;742 }743 744 // Check 'extern "*" {}' block for #include directives.745 bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {746 return true;747 }748 749 // Check 'namespace (name) {}' block for #include directives.750 bool VisitNamespaceDecl(const NamespaceDecl *D) {751 return true;752 }753 754 // Collect definition entities.755 bool VisitNamedDecl(NamedDecl *ND) {756 return true;757 }758};759 760class CompileCheckConsumer : public ASTConsumer {761public:762 CompileCheckConsumer() {}763 764 void HandleTranslationUnit(ASTContext &Ctx) override {765 CompileCheckVisitor().TraverseDecl(Ctx.getTranslationUnitDecl());766 }767};768 769class CompileCheckAction : public SyntaxOnlyAction {770public:771 CompileCheckAction() {}772 773protected:774 std::unique_ptr<clang::ASTConsumer>775 CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {776 return std::make_unique<CompileCheckConsumer>();777 }778};779 780class CompileCheckFrontendActionFactory : public FrontendActionFactory {781public:782 CompileCheckFrontendActionFactory() {}783 784 std::unique_ptr<FrontendAction> create() override {785 return std::make_unique<CompileCheckAction>();786 }787};788 789int main(int Argc, const char **Argv) {790 791 // Save program name for error messages.792 Argv0 = Argv[0];793 794 // Save program arguments for use in module.modulemap comment.795 CommandLine = std::string(sys::path::stem(sys::path::filename(Argv0)));796 for (int ArgIndex = 1; ArgIndex < Argc; ArgIndex++) {797 CommandLine.append(" ");798 CommandLine.append(Argv[ArgIndex]);799 }800 801 // This causes options to be parsed.802 cl::ParseCommandLineOptions(Argc, Argv, "modularize.\n");803 804 // No go if we have no header list file.805 if (ListFileNames.size() == 0) {806 cl::PrintHelpMessage();807 return 1;808 }809 810 std::unique_ptr<ModularizeUtilities> ModUtil;811 int HadErrors = 0;812 813 ModUtil.reset(814 ModularizeUtilities::createModularizeUtilities(815 ListFileNames, HeaderPrefix, ProblemFilesList));816 817 // Get header file names and dependencies.818 if (ModUtil->loadAllHeaderListsAndDependencies())819 HadErrors = 1;820 821 // If we are in assistant mode, output the module map and quit.822 if (ModuleMapPath.length() != 0) {823 if (!createModuleMap(ModuleMapPath, ModUtil->HeaderFileNames,824 ModUtil->ProblemFileNames,825 ModUtil->Dependencies, HeaderPrefix, RootModule))826 return 1; // Failed.827 return 0; // Success - Skip checks in assistant mode.828 }829 830 // If we're doing module maps.831 if (!NoCoverageCheck && ModUtil->HasModuleMap) {832 // Do coverage check.833 if (ModUtil->doCoverageCheck(IncludePaths, CommandLine))834 HadErrors = 1;835 }836 837 // Bail early if only doing the coverage check.838 if (CoverageCheckOnly)839 return HadErrors;840 841 // Create the compilation database.842 SmallString<256> PathBuf;843 sys::fs::current_path(PathBuf);844 std::unique_ptr<CompilationDatabase> Compilations;845 Compilations.reset(846 new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));847 848 // Create preprocessor tracker, to watch for macro and conditional problems.849 std::unique_ptr<PreprocessorTracker> PPTracker(850 PreprocessorTracker::create(ModUtil->HeaderFileNames,851 BlockCheckHeaderListOnly));852 853 // Coolect entities here.854 EntityMap Entities;855 856 // Because we can't easily determine which files failed857 // during the tool run, if we're collecting the file lists858 // for display, we do a first compile pass on individual859 // files to find which ones don't compile stand-alone.860 if (DisplayFileLists) {861 // First, make a pass to just get compile errors.862 for (auto &CompileCheckFile : ModUtil->HeaderFileNames) {863 llvm::SmallVector<std::string, 32> CompileCheckFileArray;864 CompileCheckFileArray.push_back(CompileCheckFile);865 ClangTool CompileCheckTool(*Compilations, CompileCheckFileArray);866 CompileCheckTool.appendArgumentsAdjuster(867 getModularizeArgumentsAdjuster(ModUtil->Dependencies));868 int CompileCheckFileErrors = 0;869 // FIXME: use newFrontendActionFactory.870 CompileCheckFrontendActionFactory CompileCheckFactory;871 CompileCheckFileErrors |= CompileCheckTool.run(&CompileCheckFactory);872 if (CompileCheckFileErrors != 0) {873 ModUtil->addUniqueProblemFile(CompileCheckFile); // Save problem file.874 HadErrors |= 1;875 }876 else877 ModUtil->addNoCompileErrorsFile(CompileCheckFile); // Save good file.878 }879 }880 881 // Then we make another pass on the good files to do the rest of the work.882 ClangTool Tool(*Compilations,883 (DisplayFileLists ? ModUtil->GoodFileNames : ModUtil->HeaderFileNames));884 Tool.appendArgumentsAdjuster(885 getModularizeArgumentsAdjuster(ModUtil->Dependencies));886 ModularizeFrontendActionFactory Factory(Entities, *PPTracker, HadErrors);887 HadErrors |= Tool.run(&Factory);888 889 // Create a place to save duplicate entity locations, separate bins per kind.890 typedef SmallVector<Location, 8> LocationArray;891 typedef SmallVector<LocationArray, Entry::EK_NumberOfKinds> EntryBinArray;892 EntryBinArray EntryBins;893 int KindIndex;894 for (KindIndex = 0; KindIndex < Entry::EK_NumberOfKinds; ++KindIndex) {895 LocationArray Array;896 EntryBins.push_back(Array);897 }898 899 // Check for the same entity being defined in multiple places.900 for (EntityMap::iterator E = Entities.begin(), EEnd = Entities.end();901 E != EEnd; ++E) {902 // If only one occurrence, exit early.903 if (E->second.size() == 1)904 continue;905 // Clear entity locations.906 for (EntryBinArray::iterator CI = EntryBins.begin(), CE = EntryBins.end();907 CI != CE; ++CI) {908 CI->clear();909 }910 // Walk the entities of a single name, collecting the locations,911 // separated into separate bins.912 for (unsigned I = 0, N = E->second.size(); I != N; ++I) {913 EntryBins[E->second[I].Kind].push_back(E->second[I].Loc);914 }915 // Report any duplicate entity definition errors.916 int KindIndex = 0;917 for (EntryBinArray::iterator DI = EntryBins.begin(), DE = EntryBins.end();918 DI != DE; ++DI, ++KindIndex) {919 int ECount = DI->size();920 // If only 1 occurrence of this entity, skip it, we only report duplicates.921 if (ECount <= 1)922 continue;923 LocationArray::iterator FI = DI->begin();924 StringRef kindName = Entry::getKindName((Entry::EntryKind)KindIndex);925 errs() << "error: " << kindName << " '" << E->first926 << "' defined at multiple locations:\n";927 for (LocationArray::iterator FE = DI->end(); FI != FE; ++FI) {928 errs() << " " << FI->File->getName() << ":" << FI->Line << ":"929 << FI->Column << "\n";930 ModUtil->addUniqueProblemFile(std::string(FI->File->getName()));931 }932 HadErrors = 1;933 }934 }935 936 // Complain about macro instance in header files that differ based on how937 // they are included.938 if (PPTracker->reportInconsistentMacros(errs()))939 HadErrors = 1;940 941 // Complain about preprocessor conditional directives in header files that942 // differ based on how they are included.943 if (PPTracker->reportInconsistentConditionals(errs()))944 HadErrors = 1;945 946 // Complain about any headers that have contents that differ based on how947 // they are included.948 // FIXME: Could we provide information about which preprocessor conditionals949 // are involved?950 for (auto H = Entities.HeaderContentMismatches.begin(),951 HEnd = Entities.HeaderContentMismatches.end();952 H != HEnd; ++H) {953 if (H->second.empty()) {954 errs() << "internal error: phantom header content mismatch\n";955 continue;956 }957 958 HadErrors = 1;959 ModUtil->addUniqueProblemFile(std::string(H->first.getName()));960 errs() << "error: header '" << H->first.getName()961 << "' has different contents depending on how it was included.\n";962 for (unsigned I = 0, N = H->second.size(); I != N; ++I) {963 errs() << "note: '" << H->second[I].Name << "' in "964 << H->second[I].Loc.File->getName() << " at "965 << H->second[I].Loc.Line << ":" << H->second[I].Loc.Column966 << " not always provided\n";967 }968 }969 970 if (DisplayFileLists) {971 ModUtil->displayProblemFiles();972 ModUtil->displayGoodFiles();973 ModUtil->displayCombinedFiles();974 }975 976 return HadErrors;977}978