brintos

brintos / llvm-project-archived public Read only

0
0
Text · 35.0 KiB · 33966b4 Raw
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