1d977c1e6SJohn Thompson //===- extra/modularize/Modularize.cpp - Check modularized headers --------===//
24f8ba659SJohn Thompson //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64f8ba659SJohn Thompson //
74f8ba659SJohn Thompson //===----------------------------------------------------------------------===//
84f8ba659SJohn Thompson //
98eb8d936SJohn Thompson // Introduction
108eb8d936SJohn Thompson //
114f8ba659SJohn Thompson // This file implements a tool that checks whether a set of headers provides
129cb79646SJohn Thompson // the consistent definitions required to use modules. It can also check an
139cb79646SJohn Thompson // existing module map for full coverage of the headers in a directory tree.
149cb79646SJohn Thompson //
159cb79646SJohn Thompson // For example, in examining headers, it detects whether the same entity
169cb79646SJohn Thompson // (say, a NULL macro or size_t typedef) is defined in multiple headers
179cb79646SJohn Thompson // or whether a header produces different definitions under
184f8ba659SJohn Thompson // different circumstances. These conditions cause modules built from the
194f8ba659SJohn Thompson // headers to behave poorly, and should be fixed before introducing a module
204f8ba659SJohn Thompson // map.
214f8ba659SJohn Thompson //
229cb79646SJohn Thompson // Modularize takes as input either one or more module maps (by default,
23b7ecf1c1SKazuaki Ishizaki // "module.modulemap") or one or more text files containing lists of headers
249cb79646SJohn Thompson // to check.
259cb79646SJohn Thompson //
269cb79646SJohn Thompson // In the case of a module map, the module map must be well-formed in
279cb79646SJohn Thompson // terms of syntax. Modularize will extract the header file names
289cb79646SJohn Thompson // from the map. Only normal headers are checked, assuming headers
299cb79646SJohn Thompson // marked "private", "textual", or "exclude" are not to be checked
309cb79646SJohn Thompson // as a top-level include, assuming they either are included by
319cb79646SJohn Thompson // other headers which are checked, or they are not suitable for
329cb79646SJohn Thompson // modules.
339cb79646SJohn Thompson //
349cb79646SJohn Thompson // In the case of a file list, the list is a newline-separated list of headers
359cb79646SJohn Thompson // to check with respect to each other.
36f5db45bcSJohn Thompson // Lines beginning with '#' and empty lines are ignored.
377d0213c5SJohn Thompson // Header file names followed by a colon and other space-separated
387d0213c5SJohn Thompson // file names will include those extra files as dependencies.
397d0213c5SJohn Thompson // The file names can be relative or full paths, but must be on the
407d0213c5SJohn Thompson // same line.
417d0213c5SJohn Thompson //
429cb79646SJohn Thompson // Modularize also accepts regular clang front-end arguments.
434f8ba659SJohn Thompson //
449cb79646SJohn Thompson // Usage: modularize [(modularize options)]
459cb79646SJohn Thompson // [(include-files_list)|(module map)]+ [(front-end-options) ...]
46eaa4c73dSJohn Thompson //
47eaa4c73dSJohn Thompson // Options:
48cf777e9fSJohn Thompson // -prefix=(optional header path prefix)
49eaa4c73dSJohn Thompson // Note that unless a "-prefix (header path)" option is specified,
50eaa4c73dSJohn Thompson // non-absolute file paths in the header list file will be relative
51eaa4c73dSJohn Thompson // to the header list file directory. Use -prefix to specify a
52eaa4c73dSJohn Thompson // different directory.
53cf777e9fSJohn Thompson // -module-map-path=(module map)
54eaa4c73dSJohn Thompson // Skip the checks, and instead act as a module.map generation
55eaa4c73dSJohn Thompson // assistant, generating a module map file based on the header list.
56eaa4c73dSJohn Thompson // An optional "-root-module=(rootName)" argument can specify a root
57eaa4c73dSJohn Thompson // module to be created in the generated module.map file. Note that
58eaa4c73dSJohn Thompson // you will likely need to edit this file to suit the needs of your
59eaa4c73dSJohn Thompson // headers.
604018c624SJohn Thompson // -problem-files-list=(problem files list file name)
614018c624SJohn Thompson // For use only with module map assistant. Input list of files that
624018c624SJohn Thompson // have problems with respect to modules. These will still be
634018c624SJohn Thompson // included in the generated module map, but will be marked as
644018c624SJohn Thompson // "excluded" headers.
65cf777e9fSJohn Thompson // -root-module=(root module name)
66eaa4c73dSJohn Thompson // Specifies a root module to be created in the generated module.map
67eaa4c73dSJohn Thompson // file.
68eaa4c73dSJohn Thompson // -block-check-header-list-only
69eaa4c73dSJohn Thompson // Only warn if #include directives are inside extern or namespace
70eaa4c73dSJohn Thompson // blocks if the included header is in the header list.
718eb8d936SJohn Thompson // -no-coverage-check
728eb8d936SJohn Thompson // Don't do the coverage check.
738eb8d936SJohn Thompson // -coverage-check-only
748eb8d936SJohn Thompson // Only do the coverage check.
754018c624SJohn Thompson // -display-file-lists
764018c624SJohn Thompson // Display lists of good files (no compile errors), problem files,
774018c624SJohn Thompson // and a combined list with problem files preceded by a '#'.
784018c624SJohn Thompson // This can be used to quickly determine which files have problems.
794018c624SJohn Thompson // The latter combined list might be useful in starting to modularize
804018c624SJohn Thompson // a set of headers. You can start with a full list of headers,
814018c624SJohn Thompson // use -display-file-lists option, and then use the combined list as
824018c624SJohn Thompson // your intermediate list, uncommenting-out headers as you fix them.
83a2de1088SJohn Thompson //
84f9f62b11SJohn Thompson // Note that by default, the modularize assumes .h files contain C++ source.
85f9f62b11SJohn Thompson // If your .h files in the file list contain another language, you should
86f9f62b11SJohn Thompson // append an appropriate -x option to your command line, i.e.: -x c
87fd8ca38aSJohn Thompson //
888eb8d936SJohn Thompson // Modularization Issue Checks
898eb8d936SJohn Thompson //
908eb8d936SJohn Thompson // In the process of checking headers for modularization issues, modularize
918eb8d936SJohn Thompson // will do normal parsing, reporting normal errors and warnings,
924f8ba659SJohn Thompson // but will also report special error messages like the following:
934f8ba659SJohn Thompson //
94a44f85abSJohn Thompson // error: '(symbol)' defined at multiple locations:
95a44f85abSJohn Thompson // (file):(row):(column)
964f8ba659SJohn Thompson // (file):(row):(column)
974f8ba659SJohn Thompson //
98dc118270SJohn Thompson // error: header '(file)' has different contents depending on how it was
994f8ba659SJohn Thompson // included
1004f8ba659SJohn Thompson //
1014f8ba659SJohn Thompson // The latter might be followed by messages like the following:
1024f8ba659SJohn Thompson //
1034f8ba659SJohn Thompson // note: '(symbol)' in (file) at (row):(column) not always provided
1044f8ba659SJohn Thompson //
1057c6e79f3SJohn Thompson // Checks will also be performed for macro expansions, defined(macro)
1067c6e79f3SJohn Thompson // expressions, and preprocessor conditional directives that evaluate
1077c6e79f3SJohn Thompson // inconsistently, and can produce error messages like the following:
1087c6e79f3SJohn Thompson //
1097c6e79f3SJohn Thompson // (...)/SubHeader.h:11:5:
1107c6e79f3SJohn Thompson // #if SYMBOL == 1
1117c6e79f3SJohn Thompson // ^
1127c6e79f3SJohn Thompson // error: Macro instance 'SYMBOL' has different values in this header,
1137c6e79f3SJohn Thompson // depending on how it was included.
1147c6e79f3SJohn Thompson // 'SYMBOL' expanded to: '1' with respect to these inclusion paths:
1157c6e79f3SJohn Thompson // (...)/Header1.h
1167c6e79f3SJohn Thompson // (...)/SubHeader.h
1177c6e79f3SJohn Thompson // (...)/SubHeader.h:3:9:
1187c6e79f3SJohn Thompson // #define SYMBOL 1
1197c6e79f3SJohn Thompson // ^
1207c6e79f3SJohn Thompson // Macro defined here.
1217c6e79f3SJohn Thompson // 'SYMBOL' expanded to: '2' with respect to these inclusion paths:
1227c6e79f3SJohn Thompson // (...)/Header2.h
1237c6e79f3SJohn Thompson // (...)/SubHeader.h
1247c6e79f3SJohn Thompson // (...)/SubHeader.h:7:9:
1257c6e79f3SJohn Thompson // #define SYMBOL 2
1267c6e79f3SJohn Thompson // ^
1277c6e79f3SJohn Thompson // Macro defined here.
1287c6e79f3SJohn Thompson //
12974083926SJohn Thompson // Checks will also be performed for '#include' directives that are
13074083926SJohn Thompson // nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,
13174083926SJohn Thompson // and can produce error message like the following:
13274083926SJohn Thompson //
13374083926SJohn Thompson // IncludeInExtern.h:2:3
13474083926SJohn Thompson // #include "Empty.h"
13574083926SJohn Thompson // ^
13674083926SJohn Thompson // error: Include directive within extern "C" {}.
13774083926SJohn Thompson // IncludeInExtern.h:1:1
13874083926SJohn Thompson // extern "C" {
13974083926SJohn Thompson // ^
14074083926SJohn Thompson // The "extern "C" {}" block is here.
14174083926SJohn Thompson //
1427c6e79f3SJohn Thompson // See PreprocessorTracker.cpp for additional details.
1437c6e79f3SJohn Thompson //
1448eb8d936SJohn Thompson // Module Map Coverage Check
1458eb8d936SJohn Thompson //
1468eb8d936SJohn Thompson // The coverage check uses the Clang ModuleMap class to read and parse the
1478eb8d936SJohn Thompson // module map file. Starting at the module map file directory, or just the
1488eb8d936SJohn Thompson // include paths, if specified, it will collect the names of all the files it
1498eb8d936SJohn Thompson // considers headers (no extension, .h, or .inc--if you need more, modify the
1508eb8d936SJohn Thompson // isHeader function). It then compares the headers against those referenced
1518eb8d936SJohn Thompson // in the module map, either explicitly named, or implicitly named via an
1528eb8d936SJohn Thompson // umbrella directory or umbrella file, as parsed by the ModuleMap object.
1538eb8d936SJohn Thompson // If headers are found which are not referenced or covered by an umbrella
1548eb8d936SJohn Thompson // directory or file, warning messages will be produced, and this program
1558eb8d936SJohn Thompson // will return an error code of 1. Other errors result in an error code of 2.
1568eb8d936SJohn Thompson // If no problems are found, an error code of 0 is returned.
1578eb8d936SJohn Thompson //
1588eb8d936SJohn Thompson // Note that in the case of umbrella headers, this tool invokes the compiler
1598eb8d936SJohn Thompson // to preprocess the file, and uses a callback to collect the header files
1608eb8d936SJohn Thompson // included by the umbrella header or any of its nested includes. If any
1618eb8d936SJohn Thompson // front end options are needed for these compiler invocations, these
1628eb8d936SJohn Thompson // can be included on the command line after the module map file argument.
1638eb8d936SJohn Thompson //
1648eb8d936SJohn Thompson // Warning message have the form:
1658eb8d936SJohn Thompson //
1668eb8d936SJohn Thompson // warning: module.modulemap does not account for file: Level3A.h
1678eb8d936SJohn Thompson //
1688eb8d936SJohn Thompson // Note that for the case of the module map referencing a file that does
1698eb8d936SJohn Thompson // not exist, the module map parser in Clang will (at the time of this
1708eb8d936SJohn Thompson // writing) display an error message.
1718eb8d936SJohn Thompson //
1728eb8d936SJohn Thompson // Module Map Assistant - Module Map Generation
1738eb8d936SJohn Thompson //
1745d9862f0SJohn Thompson // Modularize also has an option ("-module-map-path=module.modulemap") that will
1755d9862f0SJohn Thompson // skip the checks, and instead act as a module.modulemap generation assistant,
1765ab4f111SJohn Thompson // generating a module map file based on the header list. An optional
1775ab4f111SJohn Thompson // "-root-module=(rootName)" argument can specify a root module to be
1785d9862f0SJohn Thompson // created in the generated module.modulemap file. Note that you will likely
1795ab4f111SJohn Thompson // need to edit this file to suit the needs of your headers.
1805ab4f111SJohn Thompson //
1815d9862f0SJohn Thompson // An example command line for generating a module.modulemap file:
1825ab4f111SJohn Thompson //
1835d9862f0SJohn Thompson // modularize -module-map-path=module.modulemap -root-module=myroot \
1845d9862f0SJohn Thompson // headerlist.txt
1855ab4f111SJohn Thompson //
1865ab4f111SJohn Thompson // Note that if the headers in the header list have partial paths, sub-modules
187dd5571d5SKazuaki Ishizaki // will be created for the subdirectories involved, assuming that the
1885ab4f111SJohn Thompson // subdirectories contain headers to be grouped into a module, but still with
1895ab4f111SJohn Thompson // individual modules for the headers in the subdirectory.
1905ab4f111SJohn Thompson //
1915ab4f111SJohn Thompson // See the ModuleAssistant.cpp file comments for additional details about the
1925ab4f111SJohn Thompson // implementation of the assistant mode.
1935ab4f111SJohn Thompson //
1947475180fSJohn Thompson // Future directions:
1957475180fSJohn Thompson //
1967475180fSJohn Thompson // Basically, we want to add new checks for whatever we can check with respect
1977475180fSJohn Thompson // to checking headers for module'ability.
1987475180fSJohn Thompson //
1997475180fSJohn Thompson // Some ideas:
2007475180fSJohn Thompson //
20174083926SJohn Thompson // 1. Omit duplicate "not always provided" messages
20253a9d2daSJohn Thompson //
20374083926SJohn Thompson // 2. Add options to disable any of the checks, in case
2047475180fSJohn Thompson // there is some problem with them, or the messages get too verbose.
2057475180fSJohn Thompson //
20674083926SJohn Thompson // 3. Try to figure out the preprocessor conditional directives that
2077475180fSJohn Thompson // contribute to problems and tie them to the inconsistent definitions.
2087475180fSJohn Thompson //
20974083926SJohn Thompson // 4. There are some legitimate uses of preprocessor macros that
210f5999bdaSBob Wilson // modularize will flag as errors, such as repeatedly #include'ing
211f5999bdaSBob Wilson // a file and using interleaving defined/undefined macros
212f5999bdaSBob Wilson // to change declarations in the included file. Is there a way
213f5999bdaSBob Wilson // to address this? Maybe have modularize accept a list of macros
214f5999bdaSBob Wilson // to ignore. Otherwise you can just exclude the file, after checking
215f5999bdaSBob Wilson // for legitimate errors.
216f5999bdaSBob Wilson //
21774083926SJohn Thompson // 5. What else?
218ce601e21SJohn Thompson //
219ce601e21SJohn Thompson // General clean-up and refactoring:
220ce601e21SJohn Thompson //
221ce601e21SJohn Thompson // 1. The Location class seems to be something that we might
222ce601e21SJohn Thompson // want to design to be applicable to a wider range of tools, and stick it
223ce601e21SJohn Thompson // somewhere into Tooling/ in mainline
224ce601e21SJohn Thompson //
2254f8ba659SJohn Thompson //===----------------------------------------------------------------------===//
2264f8ba659SJohn Thompson
22785e6e871SChandler Carruth #include "Modularize.h"
228d845baecSJohn Thompson #include "ModularizeUtilities.h"
22985e6e871SChandler Carruth #include "PreprocessorTracker.h"
230f7e45c0eSChandler Carruth #include "clang/AST/ASTConsumer.h"
231d977c1e6SJohn Thompson #include "clang/AST/ASTContext.h"
232d977c1e6SJohn Thompson #include "clang/AST/RecursiveASTVisitor.h"
233d977c1e6SJohn Thompson #include "clang/Basic/SourceManager.h"
2347d0213c5SJohn Thompson #include "clang/Driver/Options.h"
235d977c1e6SJohn Thompson #include "clang/Frontend/CompilerInstance.h"
23690745210SDmitri Gribenko #include "clang/Frontend/FrontendAction.h"
237d977c1e6SJohn Thompson #include "clang/Frontend/FrontendActions.h"
238d977c1e6SJohn Thompson #include "clang/Lex/Preprocessor.h"
239d977c1e6SJohn Thompson #include "clang/Tooling/CompilationDatabase.h"
240d977c1e6SJohn Thompson #include "clang/Tooling/Tooling.h"
2417d0213c5SJohn Thompson #include "llvm/Option/Arg.h"
2427d0213c5SJohn Thompson #include "llvm/Option/ArgList.h"
2437d0213c5SJohn Thompson #include "llvm/Option/OptTable.h"
2447d0213c5SJohn Thompson #include "llvm/Option/Option.h"
245a2de1088SJohn Thompson #include "llvm/Support/CommandLine.h"
2464f8ba659SJohn Thompson #include "llvm/Support/FileSystem.h"
247f5db45bcSJohn Thompson #include "llvm/Support/MemoryBuffer.h"
248a2de1088SJohn Thompson #include "llvm/Support/Path.h"
2494f8ba659SJohn Thompson #include <algorithm>
2504f8ba659SJohn Thompson #include <iterator>
251d977c1e6SJohn Thompson #include <string>
252d977c1e6SJohn Thompson #include <vector>
2534f8ba659SJohn Thompson
254f5999bdaSBob Wilson using namespace clang;
2557d0213c5SJohn Thompson using namespace clang::driver;
2567d0213c5SJohn Thompson using namespace clang::driver::options;
2577d0213c5SJohn Thompson using namespace clang::tooling;
258a2de1088SJohn Thompson using namespace llvm;
2597d0213c5SJohn Thompson using namespace llvm::opt;
26094faa4d0SJohn Thompson using namespace Modularize;
2614f8ba659SJohn Thompson
262ea6c8dbfSJohn Thompson // Option to specify a file name for a list of header files to check.
263e7103712SBenjamin Kramer static cl::list<std::string>
264469bbc00SJohn Thompson ListFileNames(cl::Positional, cl::value_desc("list"),
265469bbc00SJohn Thompson cl::desc("<list of one or more header list files>"),
266469bbc00SJohn Thompson cl::CommaSeparated);
267ea6c8dbfSJohn Thompson
268ea6c8dbfSJohn Thompson // Collect all other arguments, which will be passed to the front end.
269e7103712SBenjamin Kramer static cl::list<std::string>
270161381e1SJohn Thompson CC1Arguments(cl::ConsumeAfter,
271161381e1SJohn Thompson cl::desc("<arguments to be passed to front end>..."));
272ea6c8dbfSJohn Thompson
273ea6c8dbfSJohn Thompson // Option to specify a prefix to be prepended to the header names.
274e7103712SBenjamin Kramer static cl::opt<std::string> HeaderPrefix(
275ea6c8dbfSJohn Thompson "prefix", cl::init(""),
276ea6c8dbfSJohn Thompson cl::desc(
277ea6c8dbfSJohn Thompson "Prepend header file paths with this prefix."
278ea6c8dbfSJohn Thompson " If not specified,"
279ea6c8dbfSJohn Thompson " the files are considered to be relative to the header list file."));
280ea6c8dbfSJohn Thompson
2815ab4f111SJohn Thompson // Option for assistant mode, telling modularize to output a module map
2825ab4f111SJohn Thompson // based on the headers list, and where to put it.
283e7103712SBenjamin Kramer static cl::opt<std::string> ModuleMapPath(
2845ab4f111SJohn Thompson "module-map-path", cl::init(""),
2855ab4f111SJohn Thompson cl::desc("Turn on module map output and specify output path or file name."
2865ab4f111SJohn Thompson " If no path is specified and if prefix option is specified,"
2875ab4f111SJohn Thompson " use prefix for file path."));
2885ab4f111SJohn Thompson
2894018c624SJohn Thompson // Option to specify list of problem files for assistant.
2904018c624SJohn Thompson // This will cause assistant to exclude these files.
2914018c624SJohn Thompson static cl::opt<std::string> ProblemFilesList(
2924018c624SJohn Thompson "problem-files-list", cl::init(""),
2934018c624SJohn Thompson cl::desc(
2944018c624SJohn Thompson "List of files with compilation or modularization problems for"
2954018c624SJohn Thompson " assistant mode. This will be excluded."));
2964018c624SJohn Thompson
2974018c624SJohn Thompson // Option for assistant mode, telling modularize the name of the root module.
298e7103712SBenjamin Kramer static cl::opt<std::string>
2995ab4f111SJohn Thompson RootModule("root-module", cl::init(""),
3005ab4f111SJohn Thompson cl::desc("Specify the name of the root module."));
3015ab4f111SJohn Thompson
302ecd3b04cSJohn Thompson // Option for limiting the #include-inside-extern-or-namespace-block
303ecd3b04cSJohn Thompson // check to only those headers explicitly listed in the header list.
304ecd3b04cSJohn Thompson // This is a work-around for private includes that purposefully get
305ecd3b04cSJohn Thompson // included inside blocks.
306ecd3b04cSJohn Thompson static cl::opt<bool>
307ecd3b04cSJohn Thompson BlockCheckHeaderListOnly("block-check-header-list-only", cl::init(false),
308ecd3b04cSJohn Thompson cl::desc("Only warn if #include directives are inside extern or namespace"
309ecd3b04cSJohn Thompson " blocks if the included header is in the header list."));
310ecd3b04cSJohn Thompson
3118eb8d936SJohn Thompson // Option for include paths for coverage check.
3128eb8d936SJohn Thompson static cl::list<std::string>
3138eb8d936SJohn Thompson IncludePaths("I", cl::desc("Include path for coverage check."),
314*d86a206fSFangrui Song cl::value_desc("path"));
3158eb8d936SJohn Thompson
316ddd7dea4SJohn Thompson // Option for disabling the coverage check.
317*d86a206fSFangrui Song static cl::opt<bool> NoCoverageCheck("no-coverage-check",
3188eb8d936SJohn Thompson cl::desc("Don't do the coverage check."));
3198eb8d936SJohn Thompson
3208eb8d936SJohn Thompson // Option for just doing the coverage check.
3218eb8d936SJohn Thompson static cl::opt<bool>
3228eb8d936SJohn Thompson CoverageCheckOnly("coverage-check-only", cl::init(false),
3238eb8d936SJohn Thompson cl::desc("Only do the coverage check."));
3248eb8d936SJohn Thompson
3254018c624SJohn Thompson // Option for displaying lists of good, bad, and mixed files.
3264018c624SJohn Thompson static cl::opt<bool>
3274018c624SJohn Thompson DisplayFileLists("display-file-lists", cl::init(false),
3284018c624SJohn Thompson cl::desc("Display lists of good files (no compile errors), problem files,"
3294018c624SJohn Thompson " and a combined list with problem files preceded by a '#'."));
3304018c624SJohn Thompson
3315ab4f111SJohn Thompson // Save the program name for error messages.
3325ab4f111SJohn Thompson const char *Argv0;
3335ab4f111SJohn Thompson // Save the command line for comments.
3345ab4f111SJohn Thompson std::string CommandLine;
335f5999bdaSBob Wilson
3367d0213c5SJohn Thompson // Helper function for finding the input file in an arguments list.
findInputFile(const CommandLineArguments & CLArgs)337e7103712SBenjamin Kramer static std::string findInputFile(const CommandLineArguments &CLArgs) {
3387d0213c5SJohn Thompson const unsigned IncludedFlagsBitmask = options::CC1Option;
3397d0213c5SJohn Thompson unsigned MissingArgIndex, MissingArgCount;
3407d0213c5SJohn Thompson SmallVector<const char *, 256> Argv;
34108124b11SPiotr Padlewski for (auto I = CLArgs.begin(), E = CLArgs.end(); I != E; ++I)
3427d0213c5SJohn Thompson Argv.push_back(I->c_str());
34343392759SIlya Biryukov InputArgList Args = getDriverOptTable().ParseArgs(
34443392759SIlya Biryukov Argv, MissingArgIndex, MissingArgCount, IncludedFlagsBitmask);
3451f02f962SDavid Blaikie std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT);
3463dcb3934SJohn Thompson return ModularizeUtilities::getCanonicalPath(Inputs.back());
3477d0213c5SJohn Thompson }
3487d0213c5SJohn Thompson
349d365731dSAlexander Kornienko // This arguments adjuster inserts "-include (file)" arguments for header
35091656d2dSJohn Thompson // dependencies. It also inserts a "-w" option and a "-x c++",
351f9f62b11SJohn Thompson // if no other "-x" option is present.
352e7103712SBenjamin Kramer static ArgumentsAdjuster
getModularizeArgumentsAdjuster(DependencyMap & Dependencies)353cf777e9fSJohn Thompson getModularizeArgumentsAdjuster(DependencyMap &Dependencies) {
3540caf6dadSAlexander Kornienko return [&Dependencies](const CommandLineArguments &Args,
3550caf6dadSAlexander Kornienko StringRef /*unused*/) {
3567d0213c5SJohn Thompson std::string InputFile = findInputFile(Args);
3577d0213c5SJohn Thompson DependentsVector &FileDependents = Dependencies[InputFile];
3587d0213c5SJohn Thompson CommandLineArguments NewArgs(Args);
359d365731dSAlexander Kornienko if (int Count = FileDependents.size()) {
3607d0213c5SJohn Thompson for (int Index = 0; Index < Count; ++Index) {
3617d0213c5SJohn Thompson NewArgs.push_back("-include");
3627d0213c5SJohn Thompson std::string File(std::string("\"") + FileDependents[Index] +
3637d0213c5SJohn Thompson std::string("\""));
3647d0213c5SJohn Thompson NewArgs.push_back(FileDependents[Index]);
3657d0213c5SJohn Thompson }
3667d0213c5SJohn Thompson }
367f9f62b11SJohn Thompson // Ignore warnings. (Insert after "clang_tool" at beginning.)
368f9f62b11SJohn Thompson NewArgs.insert(NewArgs.begin() + 1, "-w");
369f9f62b11SJohn Thompson // Since we are compiling .h files, assume C++ unless given a -x option.
37036fbd0daSFangrui Song if (!llvm::is_contained(NewArgs, "-x")) {
371f9f62b11SJohn Thompson NewArgs.insert(NewArgs.begin() + 2, "-x");
372f9f62b11SJohn Thompson NewArgs.insert(NewArgs.begin() + 3, "c++");
373f9f62b11SJohn Thompson }
374d365731dSAlexander Kornienko return NewArgs;
3757d0213c5SJohn Thompson };
376d365731dSAlexander Kornienko }
3777d0213c5SJohn Thompson
378ce601e21SJohn Thompson // FIXME: The Location class seems to be something that we might
379ce601e21SJohn Thompson // want to design to be applicable to a wider range of tools, and stick it
380ce601e21SJohn Thompson // somewhere into Tooling/ in mainline
3814f8ba659SJohn Thompson struct Location {
3824f8ba659SJohn Thompson const FileEntry *File;
3834f8ba659SJohn Thompson unsigned Line, Column;
3844f8ba659SJohn Thompson
LocationLocation3854f8ba659SJohn Thompson Location() : File(), Line(), Column() {}
3864f8ba659SJohn Thompson
LocationLocation3874f8ba659SJohn Thompson Location(SourceManager &SM, SourceLocation Loc) : File(), Line(), Column() {
3884f8ba659SJohn Thompson Loc = SM.getExpansionLoc(Loc);
3894f8ba659SJohn Thompson if (Loc.isInvalid())
3904f8ba659SJohn Thompson return;
3914f8ba659SJohn Thompson
3924f8ba659SJohn Thompson std::pair<FileID, unsigned> Decomposed = SM.getDecomposedLoc(Loc);
3934f8ba659SJohn Thompson File = SM.getFileEntryForID(Decomposed.first);
3944f8ba659SJohn Thompson if (!File)
3954f8ba659SJohn Thompson return;
3964f8ba659SJohn Thompson
3974f8ba659SJohn Thompson Line = SM.getLineNumber(Decomposed.first, Decomposed.second);
3984f8ba659SJohn Thompson Column = SM.getColumnNumber(Decomposed.first, Decomposed.second);
3994f8ba659SJohn Thompson }
4004f8ba659SJohn Thompson
operator boolLocation401f61be9c9SCraig Topper operator bool() const { return File != nullptr; }
4024f8ba659SJohn Thompson
operator ==(const Location & X,const Location & Y)4034f8ba659SJohn Thompson friend bool operator==(const Location &X, const Location &Y) {
4044f8ba659SJohn Thompson return X.File == Y.File && X.Line == Y.Line && X.Column == Y.Column;
4054f8ba659SJohn Thompson }
4064f8ba659SJohn Thompson
operator !=(const Location & X,const Location & Y)4074f8ba659SJohn Thompson friend bool operator!=(const Location &X, const Location &Y) {
4084f8ba659SJohn Thompson return !(X == Y);
4094f8ba659SJohn Thompson }
4104f8ba659SJohn Thompson
operator <(const Location & X,const Location & Y)4114f8ba659SJohn Thompson friend bool operator<(const Location &X, const Location &Y) {
4124f8ba659SJohn Thompson if (X.File != Y.File)
4134f8ba659SJohn Thompson return X.File < Y.File;
4144f8ba659SJohn Thompson if (X.Line != Y.Line)
4154f8ba659SJohn Thompson return X.Line < Y.Line;
4164f8ba659SJohn Thompson return X.Column < Y.Column;
4174f8ba659SJohn Thompson }
operator >(const Location & X,const Location & Y)418f5db45bcSJohn Thompson friend bool operator>(const Location &X, const Location &Y) { return Y < X; }
operator <=(const Location & X,const Location & Y)4194f8ba659SJohn Thompson friend bool operator<=(const Location &X, const Location &Y) {
4204f8ba659SJohn Thompson return !(Y < X);
4214f8ba659SJohn Thompson }
operator >=(const Location & X,const Location & Y)4224f8ba659SJohn Thompson friend bool operator>=(const Location &X, const Location &Y) {
4234f8ba659SJohn Thompson return !(X < Y);
4244f8ba659SJohn Thompson }
4254f8ba659SJohn Thompson };
4264f8ba659SJohn Thompson
4274f8ba659SJohn Thompson struct Entry {
42852d98865SJohn Thompson enum EntryKind {
42952d98865SJohn Thompson EK_Tag,
43052d98865SJohn Thompson EK_Value,
43152d98865SJohn Thompson EK_Macro,
43252d98865SJohn Thompson
43352d98865SJohn Thompson EK_NumberOfKinds
4344f8ba659SJohn Thompson } Kind;
4354f8ba659SJohn Thompson
4364f8ba659SJohn Thompson Location Loc;
4374e4d9b3aSJohn Thompson
getKindNameEntry4384e4d9b3aSJohn Thompson StringRef getKindName() { return getKindName(Kind); }
43952d98865SJohn Thompson static StringRef getKindName(EntryKind kind);
4404f8ba659SJohn Thompson };
4414f8ba659SJohn Thompson
4424e4d9b3aSJohn Thompson // Return a string representing the given kind.
getKindName(Entry::EntryKind kind)44352d98865SJohn Thompson StringRef Entry::getKindName(Entry::EntryKind kind) {
4444e4d9b3aSJohn Thompson switch (kind) {
44552d98865SJohn Thompson case EK_Tag:
4464e4d9b3aSJohn Thompson return "tag";
44752d98865SJohn Thompson case EK_Value:
4484e4d9b3aSJohn Thompson return "value";
44952d98865SJohn Thompson case EK_Macro:
4504e4d9b3aSJohn Thompson return "macro";
45152d98865SJohn Thompson case EK_NumberOfKinds:
4524e4d9b3aSJohn Thompson break;
4534e4d9b3aSJohn Thompson }
454c66c07d4SDavid Blaikie llvm_unreachable("invalid Entry kind");
4554e4d9b3aSJohn Thompson }
4564e4d9b3aSJohn Thompson
4574f8ba659SJohn Thompson struct HeaderEntry {
4584f8ba659SJohn Thompson std::string Name;
4594f8ba659SJohn Thompson Location Loc;
4604f8ba659SJohn Thompson
operator ==(const HeaderEntry & X,const HeaderEntry & Y)4614f8ba659SJohn Thompson friend bool operator==(const HeaderEntry &X, const HeaderEntry &Y) {
4624f8ba659SJohn Thompson return X.Loc == Y.Loc && X.Name == Y.Name;
4634f8ba659SJohn Thompson }
operator !=(const HeaderEntry & X,const HeaderEntry & Y)4644f8ba659SJohn Thompson friend bool operator!=(const HeaderEntry &X, const HeaderEntry &Y) {
4654f8ba659SJohn Thompson return !(X == Y);
4664f8ba659SJohn Thompson }
operator <(const HeaderEntry & X,const HeaderEntry & Y)4674f8ba659SJohn Thompson friend bool operator<(const HeaderEntry &X, const HeaderEntry &Y) {
4684f8ba659SJohn Thompson return X.Loc < Y.Loc || (X.Loc == Y.Loc && X.Name < Y.Name);
4694f8ba659SJohn Thompson }
operator >(const HeaderEntry & X,const HeaderEntry & Y)4704f8ba659SJohn Thompson friend bool operator>(const HeaderEntry &X, const HeaderEntry &Y) {
4714f8ba659SJohn Thompson return Y < X;
4724f8ba659SJohn Thompson }
operator <=(const HeaderEntry & X,const HeaderEntry & Y)4734f8ba659SJohn Thompson friend bool operator<=(const HeaderEntry &X, const HeaderEntry &Y) {
4744f8ba659SJohn Thompson return !(Y < X);
4754f8ba659SJohn Thompson }
operator >=(const HeaderEntry & X,const HeaderEntry & Y)4764f8ba659SJohn Thompson friend bool operator>=(const HeaderEntry &X, const HeaderEntry &Y) {
4774f8ba659SJohn Thompson return !(X < Y);
4784f8ba659SJohn Thompson }
4794f8ba659SJohn Thompson };
4804f8ba659SJohn Thompson
4814f8ba659SJohn Thompson typedef std::vector<HeaderEntry> HeaderContents;
4824f8ba659SJohn Thompson
483f5db45bcSJohn Thompson class EntityMap : public StringMap<SmallVector<Entry, 2> > {
4844f8ba659SJohn Thompson public:
485f5db45bcSJohn Thompson DenseMap<const FileEntry *, HeaderContents> HeaderContentMismatches;
4864f8ba659SJohn Thompson
add(const std::string & Name,enum Entry::EntryKind Kind,Location Loc)48740178c35SYaron Keren void add(const std::string &Name, enum Entry::EntryKind Kind, Location Loc) {
4884f8ba659SJohn Thompson // Record this entity in its header.
48940178c35SYaron Keren HeaderEntry HE = { Name, Loc };
4904f8ba659SJohn Thompson CurHeaderContents[Loc.File].push_back(HE);
4914f8ba659SJohn Thompson
4924f8ba659SJohn Thompson // Check whether we've seen this entry before.
493f5db45bcSJohn Thompson SmallVector<Entry, 2> &Entries = (*this)[Name];
4944f8ba659SJohn Thompson for (unsigned I = 0, N = Entries.size(); I != N; ++I) {
4954f8ba659SJohn Thompson if (Entries[I].Kind == Kind && Entries[I].Loc == Loc)
4964f8ba659SJohn Thompson return;
4974f8ba659SJohn Thompson }
4984f8ba659SJohn Thompson
4994f8ba659SJohn Thompson // We have not seen this entry before; record it.
5004f8ba659SJohn Thompson Entry E = { Kind, Loc };
5014f8ba659SJohn Thompson Entries.push_back(E);
5024f8ba659SJohn Thompson }
5034f8ba659SJohn Thompson
mergeCurHeaderContents()5044f8ba659SJohn Thompson void mergeCurHeaderContents() {
505f5db45bcSJohn Thompson for (DenseMap<const FileEntry *, HeaderContents>::iterator
506f5db45bcSJohn Thompson H = CurHeaderContents.begin(),
507f5db45bcSJohn Thompson HEnd = CurHeaderContents.end();
5084f8ba659SJohn Thompson H != HEnd; ++H) {
5094f8ba659SJohn Thompson // Sort contents.
5104065e921SBenjamin Kramer llvm::sort(H->second);
5114f8ba659SJohn Thompson
5124f8ba659SJohn Thompson // Check whether we've seen this header before.
513f5db45bcSJohn Thompson DenseMap<const FileEntry *, HeaderContents>::iterator KnownH =
514f5db45bcSJohn Thompson AllHeaderContents.find(H->first);
5154f8ba659SJohn Thompson if (KnownH == AllHeaderContents.end()) {
5164f8ba659SJohn Thompson // We haven't seen this header before; record its contents.
5174f8ba659SJohn Thompson AllHeaderContents.insert(*H);
5184f8ba659SJohn Thompson continue;
5194f8ba659SJohn Thompson }
5204f8ba659SJohn Thompson
5214f8ba659SJohn Thompson // If the header contents are the same, we're done.
5224f8ba659SJohn Thompson if (H->second == KnownH->second)
5234f8ba659SJohn Thompson continue;
5244f8ba659SJohn Thompson
5254f8ba659SJohn Thompson // Determine what changed.
526f5db45bcSJohn Thompson std::set_symmetric_difference(
527f5db45bcSJohn Thompson H->second.begin(), H->second.end(), KnownH->second.begin(),
5284f8ba659SJohn Thompson KnownH->second.end(),
5294f8ba659SJohn Thompson std::back_inserter(HeaderContentMismatches[H->first]));
5304f8ba659SJohn Thompson }
5314f8ba659SJohn Thompson
5324f8ba659SJohn Thompson CurHeaderContents.clear();
5334f8ba659SJohn Thompson }
534161381e1SJohn Thompson
5351f67ccb3SJohn Thompson private:
536f5db45bcSJohn Thompson DenseMap<const FileEntry *, HeaderContents> CurHeaderContents;
537f5db45bcSJohn Thompson DenseMap<const FileEntry *, HeaderContents> AllHeaderContents;
5384f8ba659SJohn Thompson };
5394f8ba659SJohn Thompson
540161381e1SJohn Thompson class CollectEntitiesVisitor
541161381e1SJohn Thompson : public RecursiveASTVisitor<CollectEntitiesVisitor> {
5424f8ba659SJohn Thompson public:
CollectEntitiesVisitor(SourceManager & SM,EntityMap & Entities,Preprocessor & PP,PreprocessorTracker & PPTracker,int & HadErrors)54374083926SJohn Thompson CollectEntitiesVisitor(SourceManager &SM, EntityMap &Entities,
54474083926SJohn Thompson Preprocessor &PP, PreprocessorTracker &PPTracker,
54574083926SJohn Thompson int &HadErrors)
54674083926SJohn Thompson : SM(SM), Entities(Entities), PP(PP), PPTracker(PPTracker),
54774083926SJohn Thompson HadErrors(HadErrors) {}
5484f8ba659SJohn Thompson
TraverseStmt(Stmt * S)5494f8ba659SJohn Thompson bool TraverseStmt(Stmt *S) { return true; }
TraverseType(QualType T)5504f8ba659SJohn Thompson bool TraverseType(QualType T) { return true; }
TraverseTypeLoc(TypeLoc TL)5514f8ba659SJohn Thompson bool TraverseTypeLoc(TypeLoc TL) { return true; }
TraverseNestedNameSpecifier(NestedNameSpecifier * NNS)5524f8ba659SJohn Thompson bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }
TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS)553f5db45bcSJohn Thompson bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
554f5db45bcSJohn Thompson return true;
555f5db45bcSJohn Thompson }
TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo)556f5db45bcSJohn Thompson bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
557f5db45bcSJohn Thompson return true;
558f5db45bcSJohn Thompson }
TraverseTemplateName(TemplateName Template)5594f8ba659SJohn Thompson bool TraverseTemplateName(TemplateName Template) { return true; }
TraverseTemplateArgument(const TemplateArgument & Arg)5604f8ba659SJohn Thompson bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
TraverseTemplateArgumentLoc(const TemplateArgumentLoc & ArgLoc)561f5db45bcSJohn Thompson bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
562f5db45bcSJohn Thompson return true;
563f5db45bcSJohn Thompson }
TraverseTemplateArguments(const TemplateArgument * Args,unsigned NumArgs)5644f8ba659SJohn Thompson bool TraverseTemplateArguments(const TemplateArgument *Args,
565f5db45bcSJohn Thompson unsigned NumArgs) {
566f5db45bcSJohn Thompson return true;
567f5db45bcSJohn Thompson }
TraverseConstructorInitializer(CXXCtorInitializer * Init)5684f8ba659SJohn Thompson bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }
TraverseLambdaCapture(LambdaExpr * LE,const LambdaCapture * C,Expr * Init)569e9a265a2SMartin Bohme bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
570e9a265a2SMartin Bohme Expr *Init) {
571e9a265a2SMartin Bohme return true;
572e9a265a2SMartin Bohme }
5734f8ba659SJohn Thompson
57474083926SJohn Thompson // Check 'extern "*" {}' block for #include directives.
VisitLinkageSpecDecl(LinkageSpecDecl * D)57574083926SJohn Thompson bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {
57674083926SJohn Thompson // Bail if not a block.
57774083926SJohn Thompson if (!D->hasBraces())
57874083926SJohn Thompson return true;
57974083926SJohn Thompson SourceRange BlockRange = D->getSourceRange();
58074083926SJohn Thompson const char *LinkageLabel;
58174083926SJohn Thompson switch (D->getLanguage()) {
58274083926SJohn Thompson case LinkageSpecDecl::lang_c:
58374083926SJohn Thompson LinkageLabel = "extern \"C\" {}";
58474083926SJohn Thompson break;
58574083926SJohn Thompson case LinkageSpecDecl::lang_cxx:
58674083926SJohn Thompson LinkageLabel = "extern \"C++\" {}";
58774083926SJohn Thompson break;
58874083926SJohn Thompson }
58974083926SJohn Thompson if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, LinkageLabel,
59074083926SJohn Thompson errs()))
59174083926SJohn Thompson HadErrors = 1;
59274083926SJohn Thompson return true;
59374083926SJohn Thompson }
59474083926SJohn Thompson
59574083926SJohn Thompson // Check 'namespace (name) {}' block for #include directives.
VisitNamespaceDecl(const NamespaceDecl * D)59674083926SJohn Thompson bool VisitNamespaceDecl(const NamespaceDecl *D) {
59774083926SJohn Thompson SourceRange BlockRange = D->getSourceRange();
59874083926SJohn Thompson std::string Label("namespace ");
59974083926SJohn Thompson Label += D->getName();
60074083926SJohn Thompson Label += " {}";
60174083926SJohn Thompson if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, Label.c_str(),
60274083926SJohn Thompson errs()))
60374083926SJohn Thompson HadErrors = 1;
60474083926SJohn Thompson return true;
60574083926SJohn Thompson }
60674083926SJohn Thompson
60774083926SJohn Thompson // Collect definition entities.
VisitNamedDecl(NamedDecl * ND)6084f8ba659SJohn Thompson bool VisitNamedDecl(NamedDecl *ND) {
6094f8ba659SJohn Thompson // We only care about file-context variables.
6104f8ba659SJohn Thompson if (!ND->getDeclContext()->isFileContext())
6114f8ba659SJohn Thompson return true;
6124f8ba659SJohn Thompson
6134f8ba659SJohn Thompson // Skip declarations that tend to be properly multiply-declared.
6144f8ba659SJohn Thompson if (isa<NamespaceDecl>(ND) || isa<UsingDirectiveDecl>(ND) ||
6154f8ba659SJohn Thompson isa<NamespaceAliasDecl>(ND) ||
616f5db45bcSJohn Thompson isa<ClassTemplateSpecializationDecl>(ND) || isa<UsingDecl>(ND) ||
6178e01c069SJohn Thompson isa<ClassTemplateDecl>(ND) || isa<TemplateTypeParmDecl>(ND) ||
618cc2e291dSJohn Thompson isa<TypeAliasTemplateDecl>(ND) || isa<UsingShadowDecl>(ND) ||
619cc2e291dSJohn Thompson isa<FunctionDecl>(ND) || isa<FunctionTemplateDecl>(ND) ||
6204f8ba659SJohn Thompson (isa<TagDecl>(ND) &&
6214f8ba659SJohn Thompson !cast<TagDecl>(ND)->isThisDeclarationADefinition()))
6224f8ba659SJohn Thompson return true;
6234f8ba659SJohn Thompson
6248e01c069SJohn Thompson // Skip anonymous declarations.
6258e01c069SJohn Thompson if (!ND->getDeclName())
6268e01c069SJohn Thompson return true;
6278e01c069SJohn Thompson
6288e01c069SJohn Thompson // Get the qualified name.
6298e01c069SJohn Thompson std::string Name;
6308e01c069SJohn Thompson llvm::raw_string_ostream OS(Name);
6318e01c069SJohn Thompson ND->printQualifiedName(OS);
6328e01c069SJohn Thompson OS.flush();
6334f8ba659SJohn Thompson if (Name.empty())
6344f8ba659SJohn Thompson return true;
6354f8ba659SJohn Thompson
6364f8ba659SJohn Thompson Location Loc(SM, ND->getLocation());
6374f8ba659SJohn Thompson if (!Loc)
6384f8ba659SJohn Thompson return true;
6394f8ba659SJohn Thompson
64052d98865SJohn Thompson Entities.add(Name, isa<TagDecl>(ND) ? Entry::EK_Tag : Entry::EK_Value, Loc);
6414f8ba659SJohn Thompson return true;
6424f8ba659SJohn Thompson }
643161381e1SJohn Thompson
6441f67ccb3SJohn Thompson private:
6451f67ccb3SJohn Thompson SourceManager &SM;
6461f67ccb3SJohn Thompson EntityMap &Entities;
64774083926SJohn Thompson Preprocessor &PP;
64874083926SJohn Thompson PreprocessorTracker &PPTracker;
64974083926SJohn Thompson int &HadErrors;
6504f8ba659SJohn Thompson };
6514f8ba659SJohn Thompson
6524f8ba659SJohn Thompson class CollectEntitiesConsumer : public ASTConsumer {
6534f8ba659SJohn Thompson public:
CollectEntitiesConsumer(EntityMap & Entities,PreprocessorTracker & preprocessorTracker,Preprocessor & PP,StringRef InFile,int & HadErrors)65494faa4d0SJohn Thompson CollectEntitiesConsumer(EntityMap &Entities,
65594faa4d0SJohn Thompson PreprocessorTracker &preprocessorTracker,
65674083926SJohn Thompson Preprocessor &PP, StringRef InFile, int &HadErrors)
65774083926SJohn Thompson : Entities(Entities), PPTracker(preprocessorTracker), PP(PP),
65874083926SJohn Thompson HadErrors(HadErrors) {
65994faa4d0SJohn Thompson PPTracker.handlePreprocessorEntry(PP, InFile);
66094faa4d0SJohn Thompson }
66194faa4d0SJohn Thompson
~CollectEntitiesConsumer()66287638f63SAlexander Kornienko ~CollectEntitiesConsumer() override { PPTracker.handlePreprocessorExit(); }
6634f8ba659SJohn Thompson
HandleTranslationUnit(ASTContext & Ctx)66487638f63SAlexander Kornienko void HandleTranslationUnit(ASTContext &Ctx) override {
6654f8ba659SJohn Thompson SourceManager &SM = Ctx.getSourceManager();
6664f8ba659SJohn Thompson
6674f8ba659SJohn Thompson // Collect declared entities.
66874083926SJohn Thompson CollectEntitiesVisitor(SM, Entities, PP, PPTracker, HadErrors)
6694f8ba659SJohn Thompson .TraverseDecl(Ctx.getTranslationUnitDecl());
6704f8ba659SJohn Thompson
6714f8ba659SJohn Thompson // Collect macro definitions.
6724f8ba659SJohn Thompson for (Preprocessor::macro_iterator M = PP.macro_begin(),
6734f8ba659SJohn Thompson MEnd = PP.macro_end();
6744f8ba659SJohn Thompson M != MEnd; ++M) {
67576e66607SRichard Smith Location Loc(SM, M->second.getLatest()->getLocation());
6764f8ba659SJohn Thompson if (!Loc)
6774f8ba659SJohn Thompson continue;
6784f8ba659SJohn Thompson
67952d98865SJohn Thompson Entities.add(M->first->getName().str(), Entry::EK_Macro, Loc);
6804f8ba659SJohn Thompson }
6814f8ba659SJohn Thompson
6824f8ba659SJohn Thompson // Merge header contents.
6834f8ba659SJohn Thompson Entities.mergeCurHeaderContents();
6844f8ba659SJohn Thompson }
685161381e1SJohn Thompson
6861f67ccb3SJohn Thompson private:
6871f67ccb3SJohn Thompson EntityMap &Entities;
68894faa4d0SJohn Thompson PreprocessorTracker &PPTracker;
6891f67ccb3SJohn Thompson Preprocessor &PP;
69074083926SJohn Thompson int &HadErrors;
6914f8ba659SJohn Thompson };
6924f8ba659SJohn Thompson
6934f8ba659SJohn Thompson class CollectEntitiesAction : public SyntaxOnlyAction {
6941f67ccb3SJohn Thompson public:
CollectEntitiesAction(EntityMap & Entities,PreprocessorTracker & preprocessorTracker,int & HadErrors)69594faa4d0SJohn Thompson CollectEntitiesAction(EntityMap &Entities,
69674083926SJohn Thompson PreprocessorTracker &preprocessorTracker,
69774083926SJohn Thompson int &HadErrors)
69874083926SJohn Thompson : Entities(Entities), PPTracker(preprocessorTracker),
69974083926SJohn Thompson HadErrors(HadErrors) {}
700161381e1SJohn Thompson
7014f8ba659SJohn Thompson protected:
702680c4c89SDavid Blaikie std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)703680c4c89SDavid Blaikie CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
7041c705d9cSJonas Devlieghere return std::make_unique<CollectEntitiesConsumer>(
705680c4c89SDavid Blaikie Entities, PPTracker, CI.getPreprocessor(), InFile, HadErrors);
7064f8ba659SJohn Thompson }
707161381e1SJohn Thompson
7081f67ccb3SJohn Thompson private:
7091f67ccb3SJohn Thompson EntityMap &Entities;
71094faa4d0SJohn Thompson PreprocessorTracker &PPTracker;
71174083926SJohn Thompson int &HadErrors;
7124f8ba659SJohn Thompson };
7134f8ba659SJohn Thompson
7144f8ba659SJohn Thompson class ModularizeFrontendActionFactory : public FrontendActionFactory {
7154f8ba659SJohn Thompson public:
ModularizeFrontendActionFactory(EntityMap & Entities,PreprocessorTracker & preprocessorTracker,int & HadErrors)71694faa4d0SJohn Thompson ModularizeFrontendActionFactory(EntityMap &Entities,
71774083926SJohn Thompson PreprocessorTracker &preprocessorTracker,
71874083926SJohn Thompson int &HadErrors)
71974083926SJohn Thompson : Entities(Entities), PPTracker(preprocessorTracker),
72074083926SJohn Thompson HadErrors(HadErrors) {}
7214f8ba659SJohn Thompson
create()72290745210SDmitri Gribenko std::unique_ptr<FrontendAction> create() override {
72390745210SDmitri Gribenko return std::make_unique<CollectEntitiesAction>(Entities, PPTracker,
72490745210SDmitri Gribenko HadErrors);
7254f8ba659SJohn Thompson }
726161381e1SJohn Thompson
7271f67ccb3SJohn Thompson private:
7281f67ccb3SJohn Thompson EntityMap &Entities;
72994faa4d0SJohn Thompson PreprocessorTracker &PPTracker;
73074083926SJohn Thompson int &HadErrors;
7314f8ba659SJohn Thompson };
7324f8ba659SJohn Thompson
7334018c624SJohn Thompson class CompileCheckVisitor
7344018c624SJohn Thompson : public RecursiveASTVisitor<CompileCheckVisitor> {
7354018c624SJohn Thompson public:
CompileCheckVisitor()7364018c624SJohn Thompson CompileCheckVisitor() {}
7374018c624SJohn Thompson
TraverseStmt(Stmt * S)7384018c624SJohn Thompson bool TraverseStmt(Stmt *S) { return true; }
TraverseType(QualType T)7394018c624SJohn Thompson bool TraverseType(QualType T) { return true; }
TraverseTypeLoc(TypeLoc TL)7404018c624SJohn Thompson bool TraverseTypeLoc(TypeLoc TL) { return true; }
TraverseNestedNameSpecifier(NestedNameSpecifier * NNS)7414018c624SJohn Thompson bool TraverseNestedNameSpecifier(NestedNameSpecifier *NNS) { return true; }
TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS)7424018c624SJohn Thompson bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
7434018c624SJohn Thompson return true;
7444018c624SJohn Thompson }
TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo)7454018c624SJohn Thompson bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
7464018c624SJohn Thompson return true;
7474018c624SJohn Thompson }
TraverseTemplateName(TemplateName Template)7484018c624SJohn Thompson bool TraverseTemplateName(TemplateName Template) { return true; }
TraverseTemplateArgument(const TemplateArgument & Arg)7494018c624SJohn Thompson bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
TraverseTemplateArgumentLoc(const TemplateArgumentLoc & ArgLoc)7504018c624SJohn Thompson bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
7514018c624SJohn Thompson return true;
7524018c624SJohn Thompson }
TraverseTemplateArguments(const TemplateArgument * Args,unsigned NumArgs)7534018c624SJohn Thompson bool TraverseTemplateArguments(const TemplateArgument *Args,
7544018c624SJohn Thompson unsigned NumArgs) {
7554018c624SJohn Thompson return true;
7564018c624SJohn Thompson }
TraverseConstructorInitializer(CXXCtorInitializer * Init)7574018c624SJohn Thompson bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }
TraverseLambdaCapture(LambdaExpr * LE,const LambdaCapture * C,Expr * Init)758e9a265a2SMartin Bohme bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
759e9a265a2SMartin Bohme Expr *Init) {
760e9a265a2SMartin Bohme return true;
761e9a265a2SMartin Bohme }
7624018c624SJohn Thompson
7634018c624SJohn Thompson // Check 'extern "*" {}' block for #include directives.
VisitLinkageSpecDecl(LinkageSpecDecl * D)7644018c624SJohn Thompson bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {
7654018c624SJohn Thompson return true;
7664018c624SJohn Thompson }
7674018c624SJohn Thompson
7684018c624SJohn Thompson // Check 'namespace (name) {}' block for #include directives.
VisitNamespaceDecl(const NamespaceDecl * D)7694018c624SJohn Thompson bool VisitNamespaceDecl(const NamespaceDecl *D) {
7704018c624SJohn Thompson return true;
7714018c624SJohn Thompson }
7724018c624SJohn Thompson
7734018c624SJohn Thompson // Collect definition entities.
VisitNamedDecl(NamedDecl * ND)7744018c624SJohn Thompson bool VisitNamedDecl(NamedDecl *ND) {
7754018c624SJohn Thompson return true;
7764018c624SJohn Thompson }
7774018c624SJohn Thompson };
7784018c624SJohn Thompson
7794018c624SJohn Thompson class CompileCheckConsumer : public ASTConsumer {
7804018c624SJohn Thompson public:
CompileCheckConsumer()7814018c624SJohn Thompson CompileCheckConsumer() {}
7824018c624SJohn Thompson
HandleTranslationUnit(ASTContext & Ctx)7834018c624SJohn Thompson void HandleTranslationUnit(ASTContext &Ctx) override {
7844018c624SJohn Thompson CompileCheckVisitor().TraverseDecl(Ctx.getTranslationUnitDecl());
7854018c624SJohn Thompson }
7864018c624SJohn Thompson };
7874018c624SJohn Thompson
7884018c624SJohn Thompson class CompileCheckAction : public SyntaxOnlyAction {
7894018c624SJohn Thompson public:
CompileCheckAction()7904018c624SJohn Thompson CompileCheckAction() {}
7914018c624SJohn Thompson
7924018c624SJohn Thompson protected:
7934018c624SJohn Thompson std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)7944018c624SJohn Thompson CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
7951c705d9cSJonas Devlieghere return std::make_unique<CompileCheckConsumer>();
7964018c624SJohn Thompson }
7974018c624SJohn Thompson };
7984018c624SJohn Thompson
7994018c624SJohn Thompson class CompileCheckFrontendActionFactory : public FrontendActionFactory {
8004018c624SJohn Thompson public:
CompileCheckFrontendActionFactory()8014018c624SJohn Thompson CompileCheckFrontendActionFactory() {}
8024018c624SJohn Thompson
create()80390745210SDmitri Gribenko std::unique_ptr<FrontendAction> create() override {
80490745210SDmitri Gribenko return std::make_unique<CompileCheckAction>();
8054018c624SJohn Thompson }
8064018c624SJohn Thompson };
8074018c624SJohn Thompson
main(int Argc,const char ** Argv)808bb0a3b06SJohn Thompson int main(int Argc, const char **Argv) {
809a2de1088SJohn Thompson
8105ab4f111SJohn Thompson // Save program name for error messages.
8115ab4f111SJohn Thompson Argv0 = Argv[0];
8125ab4f111SJohn Thompson
8135d9862f0SJohn Thompson // Save program arguments for use in module.modulemap comment.
814adcd0268SBenjamin Kramer CommandLine = std::string(sys::path::stem(sys::path::filename(Argv0)));
8155ab4f111SJohn Thompson for (int ArgIndex = 1; ArgIndex < Argc; ArgIndex++) {
8165ab4f111SJohn Thompson CommandLine.append(" ");
8175ab4f111SJohn Thompson CommandLine.append(Argv[ArgIndex]);
8185ab4f111SJohn Thompson }
8195ab4f111SJohn Thompson
820a2de1088SJohn Thompson // This causes options to be parsed.
821bb0a3b06SJohn Thompson cl::ParseCommandLineOptions(Argc, Argv, "modularize.\n");
822a2de1088SJohn Thompson
823a2de1088SJohn Thompson // No go if we have no header list file.
824469bbc00SJohn Thompson if (ListFileNames.size() == 0) {
825a2de1088SJohn Thompson cl::PrintHelpMessage();
826301faac1SAlexander Kornienko return 1;
8274f8ba659SJohn Thompson }
8284f8ba659SJohn Thompson
829d845baecSJohn Thompson std::unique_ptr<ModularizeUtilities> ModUtil;
8308eb8d936SJohn Thompson int HadErrors = 0;
831d845baecSJohn Thompson
832d845baecSJohn Thompson ModUtil.reset(
833d845baecSJohn Thompson ModularizeUtilities::createModularizeUtilities(
8344018c624SJohn Thompson ListFileNames, HeaderPrefix, ProblemFilesList));
835d845baecSJohn Thompson
8367d0213c5SJohn Thompson // Get header file names and dependencies.
83796f5551bSJohn Thompson if (ModUtil->loadAllHeaderListsAndDependencies())
83896f5551bSJohn Thompson HadErrors = 1;
8394f8ba659SJohn Thompson
8405ab4f111SJohn Thompson // If we are in assistant mode, output the module map and quit.
841e744d2b9SJohn Thompson if (ModuleMapPath.length() != 0) {
842d845baecSJohn Thompson if (!createModuleMap(ModuleMapPath, ModUtil->HeaderFileNames,
8434018c624SJohn Thompson ModUtil->ProblemFileNames,
844d845baecSJohn Thompson ModUtil->Dependencies, HeaderPrefix, RootModule))
8455ab4f111SJohn Thompson return 1; // Failed.
8465ab4f111SJohn Thompson return 0; // Success - Skip checks in assistant mode.
8475ab4f111SJohn Thompson }
8485ab4f111SJohn Thompson
8498eb8d936SJohn Thompson // If we're doing module maps.
8508eb8d936SJohn Thompson if (!NoCoverageCheck && ModUtil->HasModuleMap) {
8518eb8d936SJohn Thompson // Do coverage check.
8528eb8d936SJohn Thompson if (ModUtil->doCoverageCheck(IncludePaths, CommandLine))
8538eb8d936SJohn Thompson HadErrors = 1;
8548eb8d936SJohn Thompson }
8558eb8d936SJohn Thompson
8568eb8d936SJohn Thompson // Bail early if only doing the coverage check.
8578eb8d936SJohn Thompson if (CoverageCheckOnly)
8588eb8d936SJohn Thompson return HadErrors;
8598eb8d936SJohn Thompson
8604f8ba659SJohn Thompson // Create the compilation database.
8614f8ba659SJohn Thompson SmallString<256> PathBuf;
862f5db45bcSJohn Thompson sys::fs::current_path(PathBuf);
8636a2dc5c3SAhmed Charles std::unique_ptr<CompilationDatabase> Compilations;
864f5db45bcSJohn Thompson Compilations.reset(
865f5db45bcSJohn Thompson new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
8664f8ba659SJohn Thompson
86794faa4d0SJohn Thompson // Create preprocessor tracker, to watch for macro and conditional problems.
868ecd3b04cSJohn Thompson std::unique_ptr<PreprocessorTracker> PPTracker(
869d845baecSJohn Thompson PreprocessorTracker::create(ModUtil->HeaderFileNames,
870d845baecSJohn Thompson BlockCheckHeaderListOnly));
87194faa4d0SJohn Thompson
8724018c624SJohn Thompson // Coolect entities here.
8734f8ba659SJohn Thompson EntityMap Entities;
8744018c624SJohn Thompson
8754018c624SJohn Thompson // Because we can't easily determine which files failed
8764018c624SJohn Thompson // during the tool run, if we're collecting the file lists
8774018c624SJohn Thompson // for display, we do a first compile pass on individual
8784018c624SJohn Thompson // files to find which ones don't compile stand-alone.
8794018c624SJohn Thompson if (DisplayFileLists) {
8804018c624SJohn Thompson // First, make a pass to just get compile errors.
8814018c624SJohn Thompson for (auto &CompileCheckFile : ModUtil->HeaderFileNames) {
8824018c624SJohn Thompson llvm::SmallVector<std::string, 32> CompileCheckFileArray;
8834018c624SJohn Thompson CompileCheckFileArray.push_back(CompileCheckFile);
8844018c624SJohn Thompson ClangTool CompileCheckTool(*Compilations, CompileCheckFileArray);
8854018c624SJohn Thompson CompileCheckTool.appendArgumentsAdjuster(
8864018c624SJohn Thompson getModularizeArgumentsAdjuster(ModUtil->Dependencies));
8874018c624SJohn Thompson int CompileCheckFileErrors = 0;
88890745210SDmitri Gribenko // FIXME: use newFrontendActionFactory.
8894018c624SJohn Thompson CompileCheckFrontendActionFactory CompileCheckFactory;
8904018c624SJohn Thompson CompileCheckFileErrors |= CompileCheckTool.run(&CompileCheckFactory);
8914018c624SJohn Thompson if (CompileCheckFileErrors != 0) {
8924018c624SJohn Thompson ModUtil->addUniqueProblemFile(CompileCheckFile); // Save problem file.
8934018c624SJohn Thompson HadErrors |= 1;
8944018c624SJohn Thompson }
8954018c624SJohn Thompson else
8964018c624SJohn Thompson ModUtil->addNoCompileErrorsFile(CompileCheckFile); // Save good file.
8974018c624SJohn Thompson }
8984018c624SJohn Thompson }
8994018c624SJohn Thompson
9004018c624SJohn Thompson // Then we make another pass on the good files to do the rest of the work.
9014018c624SJohn Thompson ClangTool Tool(*Compilations,
9024018c624SJohn Thompson (DisplayFileLists ? ModUtil->GoodFileNames : ModUtil->HeaderFileNames));
903cf777e9fSJohn Thompson Tool.appendArgumentsAdjuster(
904cf777e9fSJohn Thompson getModularizeArgumentsAdjuster(ModUtil->Dependencies));
9056e914247SBenjamin Kramer ModularizeFrontendActionFactory Factory(Entities, *PPTracker, HadErrors);
9066e914247SBenjamin Kramer HadErrors |= Tool.run(&Factory);
9074f8ba659SJohn Thompson
9084e4d9b3aSJohn Thompson // Create a place to save duplicate entity locations, separate bins per kind.
9094e4d9b3aSJohn Thompson typedef SmallVector<Location, 8> LocationArray;
91052d98865SJohn Thompson typedef SmallVector<LocationArray, Entry::EK_NumberOfKinds> EntryBinArray;
9114e4d9b3aSJohn Thompson EntryBinArray EntryBins;
912bb0a3b06SJohn Thompson int KindIndex;
913bb0a3b06SJohn Thompson for (KindIndex = 0; KindIndex < Entry::EK_NumberOfKinds; ++KindIndex) {
914bb0a3b06SJohn Thompson LocationArray Array;
915bb0a3b06SJohn Thompson EntryBins.push_back(Array);
9164b249219SMichael Gottesman }
9174e4d9b3aSJohn Thompson
9184f8ba659SJohn Thompson // Check for the same entity being defined in multiple places.
9194f8ba659SJohn Thompson for (EntityMap::iterator E = Entities.begin(), EEnd = Entities.end();
9204f8ba659SJohn Thompson E != EEnd; ++E) {
9219a5134e6SAlp Toker // If only one occurrence, exit early.
9224e4d9b3aSJohn Thompson if (E->second.size() == 1)
9234f8ba659SJohn Thompson continue;
9244e4d9b3aSJohn Thompson // Clear entity locations.
9254e4d9b3aSJohn Thompson for (EntryBinArray::iterator CI = EntryBins.begin(), CE = EntryBins.end();
9264e4d9b3aSJohn Thompson CI != CE; ++CI) {
92752d98865SJohn Thompson CI->clear();
9284f8ba659SJohn Thompson }
9294e4d9b3aSJohn Thompson // Walk the entities of a single name, collecting the locations,
9304e4d9b3aSJohn Thompson // separated into separate bins.
9314e4d9b3aSJohn Thompson for (unsigned I = 0, N = E->second.size(); I != N; ++I) {
93252d98865SJohn Thompson EntryBins[E->second[I].Kind].push_back(E->second[I].Loc);
9334e4d9b3aSJohn Thompson }
9344e4d9b3aSJohn Thompson // Report any duplicate entity definition errors.
935bb0a3b06SJohn Thompson int KindIndex = 0;
9364e4d9b3aSJohn Thompson for (EntryBinArray::iterator DI = EntryBins.begin(), DE = EntryBins.end();
937bb0a3b06SJohn Thompson DI != DE; ++DI, ++KindIndex) {
938bb0a3b06SJohn Thompson int ECount = DI->size();
9398eb8d936SJohn Thompson // If only 1 occurrence of this entity, skip it, we only report duplicates.
940bb0a3b06SJohn Thompson if (ECount <= 1)
9414e4d9b3aSJohn Thompson continue;
94252d98865SJohn Thompson LocationArray::iterator FI = DI->begin();
943bb0a3b06SJohn Thompson StringRef kindName = Entry::getKindName((Entry::EntryKind)KindIndex);
9444e4d9b3aSJohn Thompson errs() << "error: " << kindName << " '" << E->first()
9454e4d9b3aSJohn Thompson << "' defined at multiple locations:\n";
94652d98865SJohn Thompson for (LocationArray::iterator FE = DI->end(); FI != FE; ++FI) {
9474e4d9b3aSJohn Thompson errs() << " " << FI->File->getName() << ":" << FI->Line << ":"
9484e4d9b3aSJohn Thompson << FI->Column << "\n";
949adcd0268SBenjamin Kramer ModUtil->addUniqueProblemFile(std::string(FI->File->getName()));
9504e4d9b3aSJohn Thompson }
9514f8ba659SJohn Thompson HadErrors = 1;
9524f8ba659SJohn Thompson }
9534f8ba659SJohn Thompson }
9544f8ba659SJohn Thompson
95594faa4d0SJohn Thompson // Complain about macro instance in header files that differ based on how
95694faa4d0SJohn Thompson // they are included.
95794faa4d0SJohn Thompson if (PPTracker->reportInconsistentMacros(errs()))
95894faa4d0SJohn Thompson HadErrors = 1;
95994faa4d0SJohn Thompson
96094faa4d0SJohn Thompson // Complain about preprocessor conditional directives in header files that
96194faa4d0SJohn Thompson // differ based on how they are included.
96294faa4d0SJohn Thompson if (PPTracker->reportInconsistentConditionals(errs()))
96394faa4d0SJohn Thompson HadErrors = 1;
96494faa4d0SJohn Thompson
9654f8ba659SJohn Thompson // Complain about any headers that have contents that differ based on how
9664f8ba659SJohn Thompson // they are included.
967ce601e21SJohn Thompson // FIXME: Could we provide information about which preprocessor conditionals
968ce601e21SJohn Thompson // are involved?
969f5db45bcSJohn Thompson for (DenseMap<const FileEntry *, HeaderContents>::iterator
9704f8ba659SJohn Thompson H = Entities.HeaderContentMismatches.begin(),
9714f8ba659SJohn Thompson HEnd = Entities.HeaderContentMismatches.end();
9724f8ba659SJohn Thompson H != HEnd; ++H) {
9734f8ba659SJohn Thompson if (H->second.empty()) {
974f5db45bcSJohn Thompson errs() << "internal error: phantom header content mismatch\n";
9754f8ba659SJohn Thompson continue;
9764f8ba659SJohn Thompson }
9774f8ba659SJohn Thompson
9784f8ba659SJohn Thompson HadErrors = 1;
979adcd0268SBenjamin Kramer ModUtil->addUniqueProblemFile(std::string(H->first->getName()));
980f5db45bcSJohn Thompson errs() << "error: header '" << H->first->getName()
98194faa4d0SJohn Thompson << "' has different contents depending on how it was included.\n";
9824f8ba659SJohn Thompson for (unsigned I = 0, N = H->second.size(); I != N; ++I) {
983161381e1SJohn Thompson errs() << "note: '" << H->second[I].Name << "' in "
984161381e1SJohn Thompson << H->second[I].Loc.File->getName() << " at "
985161381e1SJohn Thompson << H->second[I].Loc.Line << ":" << H->second[I].Loc.Column
986161381e1SJohn Thompson << " not always provided\n";
9874f8ba659SJohn Thompson }
9884f8ba659SJohn Thompson }
9894f8ba659SJohn Thompson
9904018c624SJohn Thompson if (DisplayFileLists) {
9914018c624SJohn Thompson ModUtil->displayProblemFiles();
9924018c624SJohn Thompson ModUtil->displayGoodFiles();
9934018c624SJohn Thompson ModUtil->displayCombinedFiles();
9944018c624SJohn Thompson }
9954018c624SJohn Thompson
9964f8ba659SJohn Thompson return HadErrors;
9974f8ba659SJohn Thompson }
998