diff options
author | Sylvestre Ledru <sylvestre@debian.org> | 2022-04-08 13:05:27 +0200 |
---|---|---|
committer | Sylvestre Ledru <sylvestre@debian.org> | 2022-04-08 13:05:27 +0200 |
commit | 714666cfd0ba402bc9a07f53492eaccd11553ea3 (patch) | |
tree | 096945186137181df00959ab66b88c1e44f09c56 | |
parent | f11dff66fa5dde165b562407d1eebca59080f913 (diff) | |
parent | d7d87779db4b83cf637f6d2362927ce9cbe17b15 (diff) |
Update upstream source from tag 'upstream/8.18'
Update to upstream version '8.18'
with Debian dir 995b682507fe4c3614ef935a3cdae72b40297d4b
32 files changed, 664 insertions, 87 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index acecc08..1bdd7ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,11 @@ name: IWYU CI on: - - push - - pull_request + push: + pull_request: + schedule: + # Run build of master at 03:38 every day + - cron: '38 3 * * *' defaults: run: @@ -15,7 +18,7 @@ jobs: fail-fast: false env: - LLVM_TAG: -13 + LLVM_TAG: -14 steps: - name: Install prerequisites @@ -108,19 +108,23 @@ This weirdness is tracked in [issue 100](https://github.com/include-what-you-use The original design was built for Make, but a number of alternative run modes have come up over the years. -#### Plugging into Make #### +#### Running on single source file #### -The easiest way to run IWYU over your codebase is to run +The simplest way to use IWYU is to run it against a single source file: - make -k CXX=/path/to/llvm/Debug+Asserts/bin/include-what-you-use + include-what-you-use $CXXFLAGS myfile.cc -or +where `$CXXFLAGS` are the flags you would normally pass to the compiler. - make -k CXX=/path/to/llvm/Release/bin/include-what-you-use +#### Plugging into existing build system #### -(include-what-you-use always exits with an error code, so the build system knows it didn't build a .o file. Hence the need for `-k`.) +Typically there is already a build system containing the relevant compiler flags for all source files. Replace your compiler with `include-what-you-use` to generate a large batch of IWYU advice. Depending on your build system/build tools, this can take many forms, but for a simple GNU Make system it might look like this: -Include-what-you-use only analyzes .cc (or .cpp) files built by `make`, along with their corresponding .h files. If your project has a .h file with no corresponding .cc file, IWYU will ignore it unless you use the `--check_also` switch to add it for analysis together with a .cc file. + make -k CXX=include-what-you-use CXXFLAGS="-Xiwyu --error_always" + +(The additional `-Xiwyu --error_always` switch makes `include-what-you-use` always exit with an error code, so the build system knows it didn't build a .o file. Hence the need for `-k`.) + +In this mode `include-what-you-use` only analyzes the .cc (or .cpp) files known to your build system, along with their corresponding .h files. If your project has a .h file with no corresponding .cc file, IWYU will ignore it unless you use the `--check_also` switch to add it for analysis together with a .cc file. It is possible to run IWYU against individual header files, provided the compiler flags are carefully constructed to match all includers. #### Using with CMake #### @@ -170,7 +174,7 @@ See `iwyu_tool.py --help` for more options. We also include a tool that automatically fixes up your source files based on the IWYU recommendations. This is also alpha-quality software! Here's how to use it (requires python): - make -k CXX=/path/to/llvm/Debug+Asserts/bin/include-what-you-use 2> /tmp/iwyu.out + make -k CXX=include-what-you-use CXXFLAGS="-Xiwyu --error_always" 2> /tmp/iwyu.out python fix_includes.py < /tmp/iwyu.out If you don't like the way `fix_includes.py` munges your `#include` lines, you can control its behavior via flags. `fix_includes.py --help` will give a full list, but these are some common ones: diff --git a/fix_includes.py b/fix_includes.py index 8de5775..69b7003 100755 --- a/fix_includes.py +++ b/fix_includes.py @@ -82,9 +82,7 @@ All files mentioned in the include-what-you-use script are modified, unless filenames are specified on the commandline, in which case only those files are modified. -The exit code is the number of files that were modified (or that would -be modified if --dry_run was specified) unless that number exceeds 100, -in which case 100 is returned. +The exit code is non-zero if a critical error occurs, otherwise zero. """ _COMMENT_RE = re.compile(r'\s*//.*') @@ -2453,11 +2451,12 @@ def main(argv): if flags.sort_only: if not files_to_modify: sys.exit('FATAL ERROR: -s flag requires a list of filenames') - return SortIncludesInFiles(files_to_modify, flags) + SortIncludesInFiles(files_to_modify, flags) else: - return ProcessIWYUOutput(sys.stdin, files_to_modify, flags, cwd=os.getcwd()) + ProcessIWYUOutput(sys.stdin, files_to_modify, flags, cwd=os.getcwd()) + + return 0 if __name__ == '__main__': - num_files_fixed = main(sys.argv) - sys.exit(min(num_files_fixed, 100)) + sys.exit(main(sys.argv)) diff --git a/include-what-you-use.1 b/include-what-you-use.1 index c9376c0..9114f99 100644 --- a/include-what-you-use.1 +++ b/include-what-you-use.1 @@ -1,10 +1,10 @@ -.\" t -*- coding: UTF-8 -*- +.\" t -*- coding: utf-8 -*- .\" Man page for include-what-you-use .\" .\" This file is distributed under the University of Illinois Open Source .\" License. See LICENSE.TXT for details. .\" -.TH INCLUDE-WHAT-YOU-USE 1 "2019-11-02" include-what-you-use "User Commands" +.TH INCLUDE-WHAT-YOU-USE 1 "2022-02-21" include-what-you-use "User Commands" .SH NAME include-what-you-use \- analyze includes in C and C++ source files. .SH SYNOPSIS @@ -47,6 +47,18 @@ This flag may be specified multiple times to specify multiple glob patterns. .B \-\-cxx17ns Suggest the more concise syntax for nested namespaces introduced in C++17. .TP +.BI \-\-error [=N] +Exit with error code +.IR N +(defaults to 1 if omitted) if there are \(lqinclude-what-you-use\(rq +violations. +.TP +.BI \-\-error_always [=N] +Exit with error code +.IR N +(defaults to 1 if omitted) whether there are \(lqinclude-what-you-use\(rq +violations or not (for use with \fBmake(1)\fR). +.TP .BI \-\-keep= glob Always keep the includes matched by .IR glob . @@ -113,10 +125,16 @@ is already visible in the file's transitive includes. Set verbosity. At the highest level, this will dump the AST of the source file and explain all decisions. .SH EXIT STATUS +By default .B include-what-you-use -always returns with a nonzero status code to make usage with -.BR make (1) -feasible. +exits with zero exit code unless there's a critical error, but +.B \-\-error +or +.B \-\-error_always +can be used to customize the exit code depending on invoker expectations. +See +.IR EXAMPLE\fR. + .SH MAPPING FILES Sometimes headers are not meant to be included directly, and sometimes headers are guaranteed to include other headers. @@ -240,18 +258,22 @@ issue tracker .UE on GitHub. .SH EXAMPLE -The easiest way to run +It is possible to put .B include-what-you-use -over your codebase is to run +in place of your compiler to process all source files known to your build system .PP .RS .EX -make \-k CC=include-what-you-use CXX=include-what-you-use +make \-k CC=include-what-you-use CFLAGS="-Xiwyu --error_always" +.EE + +.EX +make \-k CXX=include-what-you-use CXXFLAGS="-Xiwyu --error_always" .EE .RE .PP -The program always exits with an error code, so the build system knows that it -didn't build an object file. Hence the need for +With \fB-Xiwyu --error_always\fR the program always exits with an error code, so +the build system knows that it didn't build an object file. Hence the need for .BR -k . It only analyzes source files built by .BR make (1) @@ -1345,6 +1345,15 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { if (decl == nullptr) // only class-types are candidates for returning true return false; + // Sometimes a type points back to an implicit decl (e.g. a bultin type), + // and we can't do author-intent analysis without location information. + // Assume that it's not forward-declarable. + if (decl->isImplicit()) { + VERRS(5) << "Skipping forward-declare analysis for implicit decl: '" + << PrintableDecl(decl) << "'\n"; + return false; + } + // If we're a template specialization, we also accept // forward-declarations of the underlying template (vector<T>, not // vector<int>). @@ -1901,7 +1910,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { // If this cast requires a user-defined conversion of the from-type, look up // its return type so we can see through up/down-casts via such conversions. const Type* converted_from_type = nullptr; - if (const NamedDecl* conv_decl = expr->getConversionFunction()) { + if (const NamedDecl* conv_decl = GetConversionFunction(expr)) { converted_from_type = cast<FunctionDecl>(conv_decl)->getReturnType().getTypePtr(); } @@ -2383,12 +2392,10 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { // that, and is clearly a c++ path, is fine; its exact // contents don't matter that much. using clang::Optional; - using clang::DirectoryLookup; using clang::FileEntryRef; const FileEntry* use_file = CurrentFileEntry(); - const DirectoryLookup* curdir = nullptr; Optional<FileEntryRef> file = compiler()->getPreprocessor().LookupFile( - CurrentLoc(), "new", true, nullptr, use_file, curdir, nullptr, + CurrentLoc(), "new", true, nullptr, use_file, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, false); if (file) { preprocessor_info().FileInfoFor(use_file)->ReportFullSymbolUse( @@ -3644,14 +3651,8 @@ class IwyuAstConsumer // Check if any unrecoverable errors have occurred. // There is no point in continuing when the AST is in a bad state. - // - // EXIT_INVALIDARGS is not a great choice for the return status - // because a compile error will not have a strong connection to the - // command line arguments, but there are only 2 error codes and - // this is the least bad choice. - // TODO : Readdress when error codes are reworked. if (compiler()->getDiagnostics().hasUnrecoverableErrorOccurred()) - exit(EXIT_INVALIDARGS); + exit(EXIT_FAILURE); const set<const FileEntry*>* const files_to_report_iwyu_violations_for = preprocessor_info().files_to_report_iwyu_violations_for(); @@ -3681,8 +3682,16 @@ class IwyuAstConsumer num_edits += preprocessor_info().FileInfoFor(main_file) ->CalculateAndReportIwyuViolations(); - // We need to force the compile to fail so we can re-run. - exit(EXIT_SUCCESS_OFFSET + num_edits); + int exit_code = EXIT_SUCCESS; + if (GlobalFlags().exit_code_always) { + // If we should always fail, use --error_always value. + exit_code = GlobalFlags().exit_code_always; + } else if (num_edits > 0) { + // If there were IWYU violations, use --error value. + exit_code = GlobalFlags().exit_code_error; + } + + exit(exit_code); } void ParseFunctionTemplates(Sema& sema, TranslationUnitDecl* tu_decl) { @@ -4043,6 +4052,20 @@ class IwyuAstConsumer return Base::VisitTypedefType(type); } + bool VisitUsingType(clang::UsingType* type) { + if (CanIgnoreCurrentASTNode()) + return true; + + // UsingType is similar to TypedefType, so treat it the same. + if (CanForwardDeclareType(current_ast_node())) { + ReportDeclForwardDeclareUse(CurrentLoc(), type->getFoundDecl()); + } else { + ReportDeclUse(CurrentLoc(), type->getFoundDecl()); + } + + return Base::VisitUsingType(type); + } + // This is a superclass of RecordType and CXXRecordType. bool VisitTagType(clang::TagType* type) { if (CanIgnoreCurrentASTNode()) return true; @@ -4191,6 +4214,8 @@ using include_what_you_use::IwyuAction; using include_what_you_use::CreateCompilerInstance; int main(int argc, char **argv) { + llvm::llvm_shutdown_obj scoped_shutdown; + // X86 target is required to parse Microsoft inline assembly, so we hope it's // part of all targets. Clang parser will complain otherwise. llvm::InitializeAllTargetInfos(); @@ -4198,21 +4223,19 @@ int main(int argc, char **argv) { llvm::InitializeAllAsmParsers(); // The command line should look like - // path/to/iwyu -Xiwyu --verbose=4 [-Xiwyu --other_iwyu_flag]... CLANG_FLAGS... foo.cc + // path/to/iwyu -Xiwyu --verbose=4 [-Xiwyu --other_iwyu_flag]... \ + // CLANG_FLAGS... foo.cc OptionsParser options_parser(argc, argv); std::unique_ptr<clang::CompilerInstance> compiler(CreateCompilerInstance( options_parser.clang_argc(), options_parser.clang_argv())); - if (compiler) { - // Create and execute the frontend to generate an LLVM bitcode module. - std::unique_ptr<clang::ASTFrontendAction> action(new IwyuAction); - compiler->ExecuteAction(*action); + if (!compiler) { + return EXIT_FAILURE; } - llvm::llvm_shutdown(); + // Create and execute the frontend to generate an LLVM bitcode module. + std::unique_ptr<clang::ASTFrontendAction> action(new IwyuAction); + compiler->ExecuteAction(*action); - // We always return a failure exit code, to indicate we didn't - // successfully compile (produce a .o for) the source files we were - // given. - return 1; + return EXIT_SUCCESS; } diff --git a/iwyu_ast_util.cc b/iwyu_ast_util.cc index 81906aa..48acb5b 100644 --- a/iwyu_ast_util.cc +++ b/iwyu_ast_util.cc @@ -32,6 +32,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/IgnoreExpr.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Stmt.h" @@ -49,11 +50,14 @@ class FileEntry; using clang::ASTDumper; using clang::BlockPointerType; +using clang::CastExpr; +using clang::CXXBindTemporaryExpr; using clang::CXXConstructExpr; using clang::CXXConstructorDecl; using clang::CXXDeleteExpr; using clang::CXXDependentScopeMemberExpr; using clang::CXXDestructorDecl; +using clang::CXXMemberCallExpr; using clang::CXXMethodDecl; using clang::CXXNewExpr; using clang::CXXRecordDecl; @@ -74,12 +78,15 @@ using clang::EnumDecl; using clang::Expr; using clang::ExprWithCleanups; using clang::FileEntry; +using clang::FullExpr; using clang::FullSourceLoc; using clang::FunctionDecl; using clang::FunctionType; using clang::ImplicitCastExpr; +using clang::IgnoreExprNodes; using clang::InjectedClassNameType; using clang::LValueReferenceType; +using clang::MaterializeTemporaryExpr; using clang::MemberExpr; using clang::MemberPointerType; using clang::NamedDecl; @@ -1475,4 +1482,36 @@ TemplateArgumentListInfo GetExplicitTplArgs(const Expr* expr) { return explicit_tpl_args; } +// This is lifted from CastExpr::getConversionFunction, and naively simplified +// to work around bugs with consteval conversion functions. +const NamedDecl* GetConversionFunction(const CastExpr* expr) { + const Expr *subexpr = nullptr; + for (const CastExpr *e = expr; e; e = dyn_cast<ImplicitCastExpr>(subexpr)) { + // Skip through implicit sema nodes. + subexpr = IgnoreExprNodes(e->getSubExpr(), [](Expr* expr) { + // FullExpr is ConstantExpr + ExprWithCleanups. + if (auto* fe = dyn_cast<FullExpr>(expr)) + return fe->getSubExpr(); + + if (auto* mte = dyn_cast<MaterializeTemporaryExpr>(expr)) + return mte->getSubExpr(); + + if (auto* bte = dyn_cast<CXXBindTemporaryExpr>(expr)) + return bte->getSubExpr(); + + return expr; + }); + + // Now resolve the conversion function depending on cast kind. + if (e->getCastKind() == clang::CK_ConstructorConversion) + return cast<CXXConstructExpr>(subexpr)->getConstructor(); + + if (e->getCastKind() == clang::CK_UserDefinedConversion) { + if (auto *MCE = dyn_cast<CXXMemberCallExpr>(subexpr)) + return MCE->getMethodDecl(); + } + } + return nullptr; +} + } // namespace include_what_you_use diff --git a/iwyu_ast_util.h b/iwyu_ast_util.h index 28668f7..f4f81a0 100644 --- a/iwyu_ast_util.h +++ b/iwyu_ast_util.h @@ -816,6 +816,10 @@ const clang::FunctionType* GetCalleeFunctionType(clang::CallExpr* expr); // such a concept (declrefexpr, memberexpr), and empty list if none is present. clang::TemplateArgumentListInfo GetExplicitTplArgs(const clang::Expr* expr); +// Workaround for https://github.com/llvm/llvm-project/issues/53044. Remove this +// wrapper in favor of Expr::getConversionFunction when that is fixed upstream. +const clang::NamedDecl* GetConversionFunction(const clang::CastExpr* expr); + } // namespace include_what_you_use #endif // INCLUDE_WHAT_YOU_USE_IWYU_AST_UTIL_H_ diff --git a/iwyu_globals.cc b/iwyu_globals.cc index 96b728c..8ba6c05 100644 --- a/iwyu_globals.cc +++ b/iwyu_globals.cc @@ -98,6 +98,9 @@ static void PrintHelp(const char* extra_msg) { " --quoted_includes_first: when sorting includes, place quoted\n" " ones first.\n" " --cxx17ns: suggests the more concise syntax introduced in C++17\n" + " --error[=N]: exit with N (default: 1) for iwyu violations\n" + " --error_always[=N]: always exit with N (default: 1) (for use\n" + " with 'make -k')\n" "\n" "In addition to IWYU-specific options you can specify the following\n" "options without -Xiwyu prefix:\n" @@ -118,6 +121,22 @@ static void PrintVersion() { << "\n"; } +static bool ParseIntegerOptarg(const char* optarg, int* res) { + char* endptr = nullptr; + long val = strtol(optarg, &endptr, 10); + if (!endptr || endptr == optarg) + return false; + + if (*endptr != '\0') + return false; + + if (val > INT_MAX || val < INT_MIN) + return false; + + *res = (int)val; + return true; +} + OptionsParser::OptionsParser(int argc, char** argv) { // Separate out iwyu-specific, intercepted, and clang flags. iwyu-specific // flags are "-Xiwyu <iwyu_flag>", intercepted flags are usual clang flags @@ -168,7 +187,9 @@ CommandlineFlags::CommandlineFlags() update_comments(false), no_fwd_decls(false), quoted_includes_first(false), - cxx17ns(false) { + cxx17ns(false), + exit_code_error(EXIT_SUCCESS), + exit_code_always(EXIT_SUCCESS) { // Always keep Qt .moc includes; its moc compiler does its own IWYU analysis. keep.emplace("*.moc"); } @@ -189,6 +210,8 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) { {"no_fwd_decls", no_argument, nullptr, 'f'}, {"quoted_includes_first", no_argument, nullptr, 'q' }, {"cxx17ns", no_argument, nullptr, 'C'}, + {"error", optional_argument, nullptr, 'e'}, + {"error_always", optional_argument, nullptr, 'a'}, {nullptr, 0, nullptr, 0} }; static const char shortopts[] = "v:c:m:n"; @@ -212,7 +235,7 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) { prefix_header_include_policy = CommandlineFlags::kRemove; } else { PrintHelp("FATAL ERROR: unknown --prefix_header_includes value."); - exit(EXIT_INVALIDARGS); + exit(EXIT_FAILURE); } break; case 'h': pch_in_code = true; break; @@ -222,14 +245,33 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) { break; case 'q': quoted_includes_first = true; break; case 'C': cxx17ns = true; break; + case 'e': + if (!optarg) { + exit_code_error = EXIT_FAILURE; + } else if (!ParseIntegerOptarg(optarg, &exit_code_error)) { + PrintHelp("FATAL ERROR: --error argument must be valid integer."); + exit(EXIT_FAILURE); + } + break; + case 'a': + if (!optarg) { + exit_code_always = EXIT_FAILURE; + } else if (!ParseIntegerOptarg(optarg, &exit_code_always)) { + PrintHelp( + "FATAL ERROR: --error_always argument must be valid " + "integer."); + exit(EXIT_FAILURE); + } + break; case -1: return optind; // means 'no more input' default: PrintHelp("FATAL ERROR: unknown flag."); - exit(EXIT_INVALIDARGS); + exit(EXIT_FAILURE); break; } } - return optind; // unreachable + + CHECK_UNREACHABLE_("All switches should be handled above"); } // Though option -v prints version too, it isn't intercepted because it also @@ -251,7 +293,7 @@ static int ParseInterceptedCommandlineFlags(int argc, char** argv) { case -1: return optind; // means 'no more input' default: PrintHelp("FATAL ERROR: unknown flag."); - exit(EXIT_INVALIDARGS); + exit(EXIT_FAILURE); break; } } diff --git a/iwyu_globals.h b/iwyu_globals.h index eefb8ce..8066890 100644 --- a/iwyu_globals.h +++ b/iwyu_globals.h @@ -24,15 +24,6 @@ struct PrintingPolicy; namespace include_what_you_use { -// Exit codes. -// If invalid args are specified in any form, we return 1, -// otherwise we return 2 + the number of edits suggested. -// Of course, this means that IWYU always fails (i.e. never returns 0.) -// This is intentional, so it can be used with make -k without ever being -// considered up-to-date. -static const int EXIT_INVALIDARGS = 1; -static const int EXIT_SUCCESS_OFFSET = 2; - using std::set; using std::string; using std::vector; @@ -102,6 +93,8 @@ struct CommandlineFlags { bool no_fwd_decls; // Disable forward declarations. bool quoted_includes_first; // Place quoted includes first in sort order. bool cxx17ns; // -C: C++17 nested namespace syntax + int exit_code_error; // Exit with this code for iwyu violations. + int exit_code_always; // Always exit with this exit code. }; const CommandlineFlags& GlobalFlags(); diff --git a/iwyu_preprocessor.cc b/iwyu_preprocessor.cc index 6dc70bf..10a1ad0 100644 --- a/iwyu_preprocessor.cc +++ b/iwyu_preprocessor.cc @@ -624,9 +624,7 @@ void IwyuPreprocessorInfo::MacroDefined(const Token& id, // #undefs and re-defines a macro, but should work fine in practice.) if (macro_loc.isValid()) macros_definition_loc_[GetName(id)] = macro_loc; - for (MacroInfo::tokens_iterator it = macro->tokens_begin(); - it != macro->tokens_end(); ++it) { - const Token& token_in_macro = *it; + for (const Token& token_in_macro : macro->tokens()) { if (token_in_macro.getKind() == clang::tok::identifier && token_in_macro.getIdentifierInfo()->hasMacroDefinition()) { macros_called_from_macros_.push_back(token_in_macro); diff --git a/iwyu_test_util.py b/iwyu_test_util.py index 9bffb31..f117d66 100755 --- a/iwyu_test_util.py +++ b/iwyu_test_util.py @@ -39,19 +39,22 @@ _ACTUAL_DIAGNOSTICS_RE = re.compile(r'^(.*?):(\d+):\d+:\s*' # This is the final summary output that iwyu.cc produces when --verbose >= 1 # The summary for a given source file should appear in that source file, # surrounded by '/**** IWYU_SUMMARY' and '***** IWYU_SUMMARY */'. +# The leading summary line may also have an expected exit-code in parentheses +# after the summary marker: '/**** IWYU_SUMMARY(10)'. _EXPECTED_SUMMARY_START_RE = re.compile(r'/\*+\s*IWYU_SUMMARY') +_EXPECTED_SUMMARY_EXIT_CODE_RE = re.compile(r'/\*+\s*IWYU_SUMMARY\((\d+)\)') _EXPECTED_SUMMARY_END_RE = re.compile(r'\**\s*IWYU_SUMMARY\s*\*+/') _ACTUAL_SUMMARY_START_RE = re.compile(r'^(.*?) should add these lines:$') _ACTUAL_SUMMARY_END_RE = re.compile(r'^---$') _ACTUAL_REMOVAL_LIST_START_RE = re.compile(r'.* should remove these lines:$') _NODIFFS_RE = re.compile(r'^\((.*?) has correct #includes/fwd-decls\)$') -# This is an IWYU_ARGS line that specifies launch arguments -# for a test in its source file. -# Example: +# This is an IWYU_ARGS line that specifies launch arguments for a test in its +# source file. Example: # // IWYU_ARGS: -Xiwyu --mapping_file=... -I . _IWYU_TEST_RUN_ARGS_RE = re.compile(r'^//\sIWYU_ARGS:\s(.*)$') + def _PortableNext(iterator): if hasattr(iterator, 'next'): iterator.next() # Python 2.4-2.6 @@ -128,7 +131,7 @@ def _GetCommandOutput(command): stdout, _ = p.communicate() lines = stdout.decode("utf-8").splitlines(True) lines = [line.replace(os.linesep, '\n') for line in lines] - return lines + return p.returncode, lines def _GetMatchingLines(regex, file_names): @@ -290,6 +293,16 @@ def _GetExpectedSummaries(files): return expected_summaries +def _GetExpectedExitCode(main_file): + with open(main_file, 'r') as fh: + for line in fh: + m = _EXPECTED_SUMMARY_EXIT_CODE_RE.match(line) + if m: + res = int(m.group(1)) + return res + return None + + def _GetActualSummaries(output): """Returns a map: source file => list of iwyu summary lines.""" @@ -443,7 +456,6 @@ def TestIwyuOnRelativeFile(cc_file, cpp_files_to_check, verbose=False): if env_verbose_level: verbosity_flags = ['-Xiwyu', '--verbose=' + env_verbose_level] - # TODO(csilvers): verify that has exit-status 0. cmd = '%s %s %s %s %s' % ( _ShellQuote(_GetIwyuPath()), # Require verbose level 3 so that we can verify the individual diagnostics. @@ -457,10 +469,16 @@ def TestIwyuOnRelativeFile(cc_file, cpp_files_to_check, verbose=False): cc_file) if verbose: print('>>> Running %s' % cmd) - output = _GetCommandOutput(cmd) + exit_code, output = _GetCommandOutput(cmd) print(''.join(output)) sys.stdout.flush() # don't commingle this output with the failure output + # Verify exit code if requested + expected_exit_code = _GetExpectedExitCode(cc_file) + if expected_exit_code is not None and exit_code != expected_exit_code: + raise AssertionError('Unexpected exit code, wanted %d, was %d' % + (expected_exit_code, exit_code)) + expected_diagnostics = _GetMatchingLines( _EXPECTED_DIAGNOSTICS_RE, cpp_files_to_check) failures = _CompareExpectedAndActualDiagnostics( diff --git a/iwyu_tool.py b/iwyu_tool.py index 45b2a4a..fcb210e 100755 --- a/iwyu_tool.py +++ b/iwyu_tool.py @@ -367,8 +367,7 @@ def execute(invocations, verbose, formatter, jobs, max_load_average=0): for invocation in invocations: proc = invocation.start(verbose) print(formatter(proc.get_output())) - if proc.returncode != 2: - exit_code = 1 + exit_code = max(exit_code, proc.returncode) return exit_code pending = [] @@ -378,8 +377,7 @@ def execute(invocations, verbose, formatter, jobs, max_load_average=0): for proc in complete: pending.remove(proc) print(formatter(proc.get_output())) - if proc.returncode != 2: - exit_code = 1 + exit_code = max(exit_code, proc.returncode) # Schedule new processes if there's room. capacity = jobs - len(pending) diff --git a/iwyu_tool_test.py b/iwyu_tool_test.py index 4dbfdac..6d0864b 100755 --- a/iwyu_tool_test.py +++ b/iwyu_tool_test.py @@ -64,7 +64,11 @@ class MockInvocation(iwyu_tool.Invocation): class MockIwyuToolMain(object): """ Replacement for iwyu_tool.main to capture parsed arguments. """ def __init__(self): - self.argspec = inspect.getargspec(iwyu_tool.main).args + if hasattr(inspect, 'getfullargspec'): + getargspec = inspect.getfullargspec + else: + getargspec = inspect.getargspec + self.argspec = getargspec(iwyu_tool.main).args self.real_iwyu_tool_main = iwyu_tool.main iwyu_tool.main = self._mock self.call_args = {} @@ -131,35 +135,48 @@ class IWYUToolTests(unittest.TestCase): def test_returncode(self): invocation = MockInvocation() - invocation.will_returncode(2) + invocation.will_returncode(0) self.assertEqual(self._execute([invocation]), 0) invocation = MockInvocation() - invocation.will_returncode(7) + invocation.will_returncode(1) self.assertEqual(self._execute([invocation]), 1) + invocation = MockInvocation() + invocation.will_returncode(2) + self.assertEqual(self._execute([invocation]), 2) def test_returncode_asynchronous(self): invocations = [MockInvocation() for _ in range(100)] for invocation in invocations: - invocation.will_returncode(2) + invocation.will_returncode(0) invocation.will_block(random.random() / 100) self.assertEqual(self._execute(invocations, jobs=100), 0) invocations = [MockInvocation() for _ in range(100)] + for invocation in invocations: + invocation.will_returncode(2) + invocation.will_block(random.random() / 100) + self.assertEqual(self._execute(invocations, jobs=100), 2) + invocations = [MockInvocation() for _ in range(100)] for n, invocation in enumerate(invocations): invocation.will_returncode(6 if n == 0 else 2) invocation.will_block(random.random() / 100) - self.assertEqual(self._execute(invocations, jobs=100), 1) + self.assertEqual(self._execute(invocations, jobs=100), 6) - def test_order_synchronous(self): + def test_returncode_synchronous(self): invocations = [MockInvocation() for _ in range(1)] for invocation in invocations: - invocation.will_returncode(2) + invocation.will_returncode(0) invocation.will_block(random.random() / 100) self.assertEqual(self._execute(invocations, jobs=100), 0) invocations = [MockInvocation() for _ in range(1)] + for invocation in invocations: + invocation.will_returncode(2) + invocation.will_block(random.random() / 100) + self.assertEqual(self._execute(invocations, jobs=100), 2) + invocations = [MockInvocation() for _ in range(1)] for n, invocation in enumerate(invocations): invocation.will_returncode(6 if n == 0 else 2) invocation.will_block(random.random() / 100) - self.assertEqual(self._execute(invocations, jobs=100), 1) + self.assertEqual(self._execute(invocations, jobs=100), 6) @unittest.skipIf(sys.platform.startswith('win'), "POSIX only") def test_is_subpath_of_posix(self): diff --git a/iwyu_version.h b/iwyu_version.h index 7c3b41c..66b7c62 100644 --- a/iwyu_version.h +++ b/iwyu_version.h @@ -10,6 +10,6 @@ #ifndef INCLUDE_WHAT_YOU_USE_IWYU_VERSION_H_ #define INCLUDE_WHAT_YOU_USE_IWYU_VERSION_H_ -#define IWYU_VERSION_STRING "0.17" +#define IWYU_VERSION_STRING "0.18" #endif // INCLUDE_WHAT_YOU_USE_IWYU_VERSION_H_ diff --git a/run_iwyu_tests.py b/run_iwyu_tests.py index 9e7918f..1eeb139 100755 --- a/run_iwyu_tests.py +++ b/run_iwyu_tests.py @@ -132,6 +132,9 @@ if __name__ == '__main__': @GenerateTests(rootdir='tests/cxx', pattern='*.cc') class cxx(unittest.TestCase): pass + @GenerateTests(rootdir='tests/driver', pattern='*.c') + class driver(unittest.TestCase): pass + if runner_args.list_tests: exit(PrintLoadedTests()) elif runner_args.list_test_files: diff --git a/tests/cxx/consteval.cc b/tests/cxx/consteval.cc new file mode 100644 index 0000000..3d4652b --- /dev/null +++ b/tests/cxx/consteval.cc @@ -0,0 +1,51 @@ +//===--- consteval.cc - test input file for iwyu --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -I . -std=c++20 + +// These tests are not particularly interesting in themselves, but they cover an +// upstream bug in Clang (https://github.com/llvm/llvm-project/issues/53044) for +// which we have a workaround. + +#include "tests/cxx/direct.h" + +struct X { + // IWYU: IndirectClass needs a declaration + consteval X(const IndirectClass& v) { + } + + // IWYU: IndirectClass needs a declaration + consteval operator IndirectClass*() const { + return nullptr; + } +}; + +void t() { + // Pass value through Consteval conversion constructor. + // IWYU: IndirectClass is...*indirect.h + IndirectClass a; + X x = a; + + // Try an implicit consteval user-defined conversion too. + // IWYU: IndirectClass needs a declaration + IndirectClass* b = x; +} + +/**** IWYU_SUMMARY + +tests/cxx/consteval.cc should add these lines: +#include "tests/cxx/indirect.h" + +tests/cxx/consteval.cc should remove these lines: +- #include "tests/cxx/direct.h" // lines XX-XX + +The full include-list for tests/cxx/consteval.cc: +#include "tests/cxx/indirect.h" // for IndirectClass + +***** IWYU_SUMMARY */ diff --git a/tests/driver/direct.h b/tests/driver/direct.h new file mode 100644 index 0000000..c4c6c78 --- /dev/null +++ b/tests/driver/direct.h @@ -0,0 +1,15 @@ +//===--- direct.h - test input file for iwyu ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef INCLUDE_WHAT_YOU_USE_TESTS_DRIVER_DIRECT_H_ +#define INCLUDE_WHAT_YOU_USE_TESTS_DRIVER_DIRECT_H_ + +#include "tests/driver/indirect.h" + +#endif // INCLUDE_WHAT_YOU_USE_TESTS_DRIVER_DIRECT_H_ diff --git a/tests/driver/exitcode_bad_args.c b/tests/driver/exitcode_bad_args.c new file mode 100644 index 0000000..4aaff4b --- /dev/null +++ b/tests/driver/exitcode_bad_args.c @@ -0,0 +1,16 @@ +//===--- exitcode_bad_args.c - test input file for iwyu -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -some -unsupported -Xiwyu -arguments + +// When argument parsing fails, IWYU exits with code 1. + +/**** IWYU_SUMMARY(1) + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_good.c b/tests/driver/exitcode_good.c new file mode 100644 index 0000000..6eb542f --- /dev/null +++ b/tests/driver/exitcode_good.c @@ -0,0 +1,16 @@ +//===--- exitcode_good.c - test input file for iwyu -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Tests that IWYU exits with code 0 when no suggestions are made. + +/**** IWYU_SUMMARY(0) + +(tests/driver/exitcode_good.c has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_good_error.c b/tests/driver/exitcode_good_error.c new file mode 100644 index 0000000..440b3ed --- /dev/null +++ b/tests/driver/exitcode_good_error.c @@ -0,0 +1,19 @@ +//===--- exitcode_good_error.c - test input file for iwyu -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -Xiwyu --error + +// When --error is provided, IWYU exits with error only if analysis finds IWYU +// violations. In this case there are none, so IWYU exits with 0. + +/**** IWYU_SUMMARY(0) + +(tests/driver/exitcode_good_error.c has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_good_error_always.c b/tests/driver/exitcode_good_error_always.c new file mode 100644 index 0000000..475e014 --- /dev/null +++ b/tests/driver/exitcode_good_error_always.c @@ -0,0 +1,19 @@ +//===--- exitcode_good_error_always.c - test input file for iwyu ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -Xiwyu --error_always + +// When --error_always is provided, IWYU exits with error even if analysis +// succeeds. + +/**** IWYU_SUMMARY(1) + +(tests/driver/exitcode_good_error_always.c has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_good_error_always_arg.c b/tests/driver/exitcode_good_error_always_arg.c new file mode 100644 index 0000000..b76c2fe --- /dev/null +++ b/tests/driver/exitcode_good_error_always_arg.c @@ -0,0 +1,19 @@ +//===--- exitcode_good_error_always_arg.c - test input file for iwyu ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -Xiwyu --error_always=100 + +// When --error_always is provided, IWYU exits with error even if analysis +// succeeds. + +/**** IWYU_SUMMARY(100) + +(tests/driver/exitcode_good_error_always_arg.c has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_good_error_and_always.c b/tests/driver/exitcode_good_error_and_always.c new file mode 100644 index 0000000..29310dc --- /dev/null +++ b/tests/driver/exitcode_good_error_and_always.c @@ -0,0 +1,18 @@ +//===--- exitcode_good_error_and_always.c - test input file for iwyu ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -Xiwyu --error=19 -Xiwyu --error_always=91 + +// When both are provided, --error_always takes precedence + +/**** IWYU_SUMMARY(91) + +(tests/driver/exitcode_good_error_and_always.c has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_good_error_arg.c b/tests/driver/exitcode_good_error_arg.c new file mode 100644 index 0000000..093a754 --- /dev/null +++ b/tests/driver/exitcode_good_error_arg.c @@ -0,0 +1,19 @@ +//===--- exitcode_good_error_arg.c - test input file for iwyu -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -Xiwyu --error=100 + +// When --error is provided, IWYU exits with error only if analysis finds IWYU +// violations. In this case there are none, so IWYU exits with 0. + +/**** IWYU_SUMMARY(0) + +(tests/driver/exitcode_good_error_arg.c has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_syntax_error.c b/tests/driver/exitcode_syntax_error.c new file mode 100644 index 0000000..6884c22 --- /dev/null +++ b/tests/driver/exitcode_syntax_error.c @@ -0,0 +1,19 @@ +//===--- exitcode_syntax_error.c - test input file for iwyu ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Tests that IWYU exits with code 1 when Clang fails to parse the provided +// source code. + +// IWYU: expected ';' after top level declarator +// IWYU: unknown type name 'this' +this is not valid C code; + +/**** IWYU_SUMMARY(1) + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_warn.c b/tests/driver/exitcode_warn.c new file mode 100644 index 0000000..3cd3499 --- /dev/null +++ b/tests/driver/exitcode_warn.c @@ -0,0 +1,30 @@ +//===--- exitcode_warn.c - test input file for iwyu -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -I . + +// By default, if IWYU finds policy violations it exits with code 0. + +#include "tests/driver/direct.h" + +// IWYU: Indirect is...*indirect.h +struct Indirect x; + +/**** IWYU_SUMMARY(0) + +tests/driver/exitcode_warn.c should add these lines: +#include "tests/driver/indirect.h" + +tests/driver/exitcode_warn.c should remove these lines: +- #include "tests/driver/direct.h" // lines XX-XX + +The full include-list for tests/driver/exitcode_warn.c: +#include "tests/driver/indirect.h" // for Indirect + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_warn_error.c b/tests/driver/exitcode_warn_error.c new file mode 100644 index 0000000..60fc1e3 --- /dev/null +++ b/tests/driver/exitcode_warn_error.c @@ -0,0 +1,31 @@ +//===--- exitcode_warn_error.c - test input file for iwyu -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -I . -Xiwyu --error + +// When --error is provided, IWYU exits with error if analysis finds IWYU +// violations. Default exit code is 1. + +#include "tests/driver/direct.h" + +// IWYU: Indirect is...*indirect.h +struct Indirect x; + +/**** IWYU_SUMMARY(1) + +tests/driver/exitcode_warn_error.c should add these lines: +#include "tests/driver/indirect.h" + +tests/driver/exitcode_warn_error.c should remove these lines: +- #include "tests/driver/direct.h" // lines XX-XX + +The full include-list for tests/driver/exitcode_warn_error.c: +#include "tests/driver/indirect.h" // for Indirect + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_warn_error_always.c b/tests/driver/exitcode_warn_error_always.c new file mode 100644 index 0000000..b1e08f7 --- /dev/null +++ b/tests/driver/exitcode_warn_error_always.c @@ -0,0 +1,32 @@ +//===--- exitcode_warn_error_always.c - test input file for iwyu ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -I . -Xiwyu --error_always + +// When --error_always is provided, IWYU exits with error whether or not there +// are IWYU violations. See exitcode_good_error_always.c for complementary +// testcase where there are no violations. + +#include "tests/driver/direct.h" + +// IWYU: Indirect is...*indirect.h +struct Indirect x; + +/**** IWYU_SUMMARY(1) + +tests/driver/exitcode_warn_error_always.c should add these lines: +#include "tests/driver/indirect.h" + +tests/driver/exitcode_warn_error_always.c should remove these lines: +- #include "tests/driver/direct.h" // lines XX-XX + +The full include-list for tests/driver/exitcode_warn_error_always.c: +#include "tests/driver/indirect.h" // for Indirect + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_warn_error_always_arg.c b/tests/driver/exitcode_warn_error_always_arg.c new file mode 100644 index 0000000..4a2c590 --- /dev/null +++ b/tests/driver/exitcode_warn_error_always_arg.c @@ -0,0 +1,32 @@ +//===--- exitcode_warn_error_always_arg.c - test input file for iwyu ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -I . -Xiwyu --error_always=11 + +// When --error_always is provided, IWYU exits with error whether or not there +// are IWYU violations. See exitcode_good_error_always_arg.c for complementary +// testcase where there are no violations. + +#include "tests/driver/direct.h" + +// IWYU: Indirect is...*indirect.h +struct Indirect x; + +/**** IWYU_SUMMARY(11) + +tests/driver/exitcode_warn_error_always_arg.c should add these lines: +#include "tests/driver/indirect.h" + +tests/driver/exitcode_warn_error_always_arg.c should remove these lines: +- #include "tests/driver/direct.h" // lines XX-XX + +The full include-list for tests/driver/exitcode_warn_error_always_arg.c: +#include "tests/driver/indirect.h" // for Indirect + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_warn_error_and_always.c b/tests/driver/exitcode_warn_error_and_always.c new file mode 100644 index 0000000..0e730ee --- /dev/null +++ b/tests/driver/exitcode_warn_error_and_always.c @@ -0,0 +1,30 @@ +//===--- exitcode_warn_error_and_always.c - test input file for iwyu ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -I . -Xiwyu --error=15 -Xiwyu --error_always=51 + +// When both are provided, --error_always takes precedence + +#include "tests/driver/direct.h" + +// IWYU: Indirect is...*indirect.h +struct Indirect x; + +/**** IWYU_SUMMARY(51) + +tests/driver/exitcode_warn_error_and_always.c should add these lines: +#include "tests/driver/indirect.h" + +tests/driver/exitcode_warn_error_and_always.c should remove these lines: +- #include "tests/driver/direct.h" // lines XX-XX + +The full include-list for tests/driver/exitcode_warn_error_and_always.c: +#include "tests/driver/indirect.h" // for Indirect + +***** IWYU_SUMMARY */ diff --git a/tests/driver/exitcode_warn_error_arg.c b/tests/driver/exitcode_warn_error_arg.c new file mode 100644 index 0000000..835d45c --- /dev/null +++ b/tests/driver/exitcode_warn_error_arg.c @@ -0,0 +1,31 @@ +//===--- exitcode_warn_error_arg.c - test input file for iwyu -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// IWYU_ARGS: -I . -Xiwyu --error=42 + +// When --error is provided, IWYU exits with error if analysis finds IWYU +// violations. + +#include "tests/driver/direct.h" + +// IWYU: Indirect is...*indirect.h +struct Indirect x; + +/**** IWYU_SUMMARY(42) + +tests/driver/exitcode_warn_error_arg.c should add these lines: +#include "tests/driver/indirect.h" + +tests/driver/exitcode_warn_error_arg.c should remove these lines: +- #include "tests/driver/direct.h" // lines XX-XX + +The full include-list for tests/driver/exitcode_warn_error_arg.c: +#include "tests/driver/indirect.h" // for Indirect + +***** IWYU_SUMMARY */ diff --git a/tests/driver/indirect.h b/tests/driver/indirect.h new file mode 100644 index 0000000..cc93fcb --- /dev/null +++ b/tests/driver/indirect.h @@ -0,0 +1,17 @@ +//===--- indirect.h - test input file for iwyu ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef INCLUDE_WHAT_YOU_USE_TESTS_DRIVER_INDIRECT_H_ +#define INCLUDE_WHAT_YOU_USE_TESTS_DRIVER_INDIRECT_H_ + +struct Indirect { + int a; +}; + +#endif // INCLUDE_WHAT_YOU_USE_TESTS_DRIVER_INDIRECT_H_ |