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 | d7d87779db4b83cf637f6d2362927ce9cbe17b15 (patch) | |
tree | a85efdb0e4b7848f98644b819cb0e972a4dff71b | |
parent | eb4e8ebe6a123e02853a1cbd5628997400d77ab6 (diff) |
New upstream version 8.18upstream/8.18
58 files changed, 1669 insertions, 268 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f06014..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: -12 + LLVM_TAG: -14 steps: - name: Install prerequisites diff --git a/CMakeLists.txt b/CMakeLists.txt index 923b4d9..0a0d683 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,9 +68,9 @@ endif() set(LLVM_LINK_COMPONENTS Option Support - X86AsmParser - X86Desc - X86Info + AllTargetsAsmParsers + AllTargetsDescs + AllTargetsInfos ) add_llvm_executable(include-what-you-use @@ -110,8 +110,8 @@ if (MSVC) -D_HAS_EXCEPTIONS=0 ) - # Enable bigobj support and sane C++ exception semantics. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /EHsc") + # Enable bigobj support + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") endif() # Link dynamically or statically depending on user preference. @@ -40,6 +40,8 @@ We assume you already have compiled LLVM and Clang libraries on your system, eit | 9 | 0.13 | `clang_9.0` | | 10 | 0.14 | `clang_10` | | 11 | 0.15 | `clang_11` | +| 12 | 0.16 | `clang_12` | +| 13 | 0.17 | `clang_13` | | ... | ... | ... | | main | | `master` | @@ -106,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 #### @@ -129,12 +135,16 @@ The `CMAKE_CXX_INCLUDE_WHAT_YOU_USE` option enables a mode where CMake first com Use it like this: mkdir build && cd build - CC="clang" CXX="clang++" cmake -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="path/to/iwyu;-Xiwyu;any;-Xiwyu;iwyu;-Xiwyu;args" ... + CC="clang" CXX="clang++" cmake -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=include-what-you-use ... or, on Windows systems: mkdir build && cd build - cmake -DCMAKE_CXX_COMPILER="%VCINSTALLDIR%/bin/cl.exe" -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="path/to/iwyu;-Xiwyu;any;-Xiwyu;iwyu;-Xiwyu;args" -G Ninja ... + cmake -DCMAKE_CXX_COMPILER="%VCINSTALLDIR%/bin/cl.exe" -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=include-what-you-use -G Ninja ... + +These examples assume that `include-what-you-use` is in the `PATH`. If it isn't, consider changing the value to an absolute path. Arguments to IWYU can be added using CMake's semicolon-separated list syntax, e.g.: + + ... cmake -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-w;-Xiwyu;--verbose=7" ... The option appears to be separately supported for both C and C++, so use `CMAKE_C_INCLUDE_WHAT_YOU_USE` for C code. @@ -142,7 +152,7 @@ Note that with Microsoft's Visual C++ compiler, IWYU needs the `--driver-mode=cl #### Using with a compilation database #### -The `iwyu_tool.py` script predates the native CMake support, and works off the [compilation database format](https://clang.llvm.org/docs/JSONCompilationDatabase.html). For example, CMake generates such a database named `compile_commands.json` with the `CMAKE_EXPORT_COMPILE_COMMANDS` option enabled. +The `iwyu_tool.py` script pre-dates the native CMake support, and works off the [compilation database format](https://clang.llvm.org/docs/JSONCompilationDatabase.html). For example, CMake generates such a database named `compile_commands.json` with the `CMAKE_EXPORT_COMPILE_COMMANDS` option enabled. The script's command-line syntax is designed to mimic Clang's LibTooling, but they are otherwise unrelated. It can be used like this: @@ -164,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 7600361..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*//.*') @@ -2309,11 +2307,12 @@ def ProcessIWYUOutput(f, files_to_process, flags, cwd): # seen for them. (We have to wait until we're all done, since a .h # file may have a contentful change when #included from one .cc # file, but not another, and we need to have merged them above.) - for filename in iwyu_output_records: - if not iwyu_output_records[filename].HasContentfulChanges(): - print('(skipping %s: iwyu reports no contentful changes)' % filename) - # Mark that we're skipping this file by setting the record to None - iwyu_output_records[filename] = None + if not flags.update_comments: + for filename in iwyu_output_records: + if not iwyu_output_records[filename].HasContentfulChanges(): + print('(skipping %s: iwyu reports no contentful changes)' % filename) + # Mark that we're skipping this file by setting the record to None + iwyu_output_records[filename] = None # Now do all the fixing, and return the number of files modified contentful_records = [ior for ior in iwyu_output_records.values() if ior] @@ -2373,6 +2372,12 @@ def main(argv): help='Put comments after the #include lines') parser.add_option('--nocomments', action='store_false', dest='comments') + parser.add_option('--update_comments', action='store_true', default=False, + help=('Update #include comments, even if no #include lines' + ' are added or removed')) + parser.add_option('--noupdate_comments', action='store_false', + dest='update_comments') + parser.add_option('--safe_headers', action='store_true', default=True, help=('Do not remove unused #includes/fwd-declares from' ' header files; just add new ones [default]')) @@ -2440,14 +2445,18 @@ def main(argv): not flags.separate_project_includes.endswith('/')): flags.separate_project_includes += os.path.sep + if flags.update_comments: + flags.comments = True + 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/fix_includes_test.py b/fix_includes_test.py index 380da54..07bd78d 100755 --- a/fix_includes_test.py +++ b/fix_includes_test.py @@ -34,6 +34,7 @@ class FakeFlags(object): def __init__(self): self.blank_lines = False self.comments = True + self.update_comments = False self.dry_run = False self.ignore_re = None self.only_re = None @@ -1916,6 +1917,54 @@ The full include-list for subdir/include_comments.cc: self.RegisterFileContents({'subdir/include_comments.cc': infile}) self.ProcessAndTest(iwyu_output) + def testUpdateCommentsFlag(self): + """Tests we update comments with --update_comments.""" + self.flags.update_comments = True + infile = """\ +#include "must_keep.h" // IWYU pragma: keep +#include "used.h" // for SomethingElse ///- +///+#include "used.h" // for Used + +Used used; +int main() { return 0; } +""" + iwyu_output = """\ +subdir/include_comments.cc should add these lines: + +subdir/include_comments.cc should remove these lines: + +The full include-list for subdir/include_comments.cc: +#include "must_keep.h" +#include "used.h" // for Used +--- +""" + self.RegisterFileContents({'subdir/include_comments.cc': infile}) + self.ProcessAndTest(iwyu_output) + + def testNoUpdateCommentsFlag(self): + """Tests we don't update comments with --noupdate_comments.""" + self.flags.update_comments = False + infile = """\ +#include "must_keep.h" // IWYU pragma: keep +#include "used.h" // for SomethingElse + +Used used; +int main() { return 0; } +""" + iwyu_output = """\ +subdir/include_comments.cc should add these lines: + +subdir/include_comments.cc should remove these lines: + +The full include-list for subdir/include_comments.cc: +#include "must_keep.h" +#include "used.h" // for Used +--- +""" + self.RegisterFileContents({'subdir/include_comments.cc': infile}) + self.ProcessAndTest(iwyu_output, + unedited_files=['subdir/include_comments.cc']) + def testFixingTwoFiles(self): """Make sure data for one fix doesn't overlap with a second.""" file_a = """\ diff --git a/gcc.libc.imp b/gcc.libc.imp index 1d436f5..a6493f6 100644 --- a/gcc.libc.imp +++ b/gcc.libc.imp @@ -203,4 +203,6 @@ { include: [ "<linux/limits.h>", private, "<limits.h>", public ] }, # PATH_MAX { include: [ "<linux/prctl.h>", private, "<sys/prctl.h>", public ] }, { include: [ "<sys/ucontext.h>", private, "<ucontext.h>", public ] }, + # Exports guaranteed by the C standard + { include: [ "<stdint.h>", public, "<inttypes.h>", public ] }, ] diff --git a/gcc.symbols.imp b/gcc.symbols.imp index 5ecf219..616fdef 100644 --- a/gcc.symbols.imp +++ b/gcc.symbols.imp @@ -9,22 +9,36 @@ # equivalents. # In each case, I ordered them so <sys/types.h> was first, if it was # an option for this type. That's the preferred #include all else -# equal. The visibility on the symbol-name is ignored; by convention -# we always set it to private. +# equal. The same goes for <stdint.h>. The visibility on the +# symbol-name is ignored; by convention we always set it to private. [ - { symbol: [ "blksize_t", private, "<sys/types.h>", public ] }, - { symbol: [ "blkcnt_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "aiocb", private, "<aio.h>", public ] }, { symbol: [ "blkcnt_t", private, "<sys/types.h>", public ] }, + { symbol: [ "blkcnt_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "blksize_t", private, "<sys/types.h>", public ] }, { symbol: [ "blksize_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "cc_t", private, "<termios.h>", public ] }, { symbol: [ "clock_t", private, "<sys/types.h>", public ] }, + { symbol: [ "clock_t", private, "<sys/time.h>", public ] }, { symbol: [ "clock_t", private, "<time.h>", public ] }, + { symbol: [ "clockid_t", private, "<sys/types.h>", public ] }, + { symbol: [ "clockid_t", private, "<time.h>", public ] }, { symbol: [ "daddr_t", private, "<sys/types.h>", public ] }, { symbol: [ "daddr_t", private, "<rpc/types.h>", public ] }, { symbol: [ "dev_t", private, "<sys/types.h>", public ] }, { symbol: [ "dev_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "div_t", private, "<stdlib.h>", public ] }, + { symbol: [ "double_t", private, "<math.h>", public ] }, { symbol: [ "error_t", private, "<errno.h>", public ] }, { symbol: [ "error_t", private, "<argp.h>", public ] }, { symbol: [ "error_t", private, "<argz.h>", public ] }, + { symbol: [ "fd_set", private, "<sys/select.h>", public ] }, + { symbol: [ "fd_set", private, "<sys/time.h>", public ] }, + { symbol: [ "fenv_t", private, "<fenv.h>", public ] }, + { symbol: [ "fexcept_t", private, "<fenv.h>", public ] }, + { symbol: [ "FILE", private, "<stdio.h>", public ] }, + { symbol: [ "FILE", private, "<wchar.h>", public ] }, + { symbol: [ "float_t", private, "<math.h>", public ] }, { symbol: [ "fsblkcnt_t", private, "<sys/types.h>", public ] }, { symbol: [ "fsblkcnt_t", private, "<sys/statvfs.h>", public ] }, { symbol: [ "fsfilcnt_t", private, "<sys/types.h>", public ] }, @@ -32,18 +46,21 @@ { symbol: [ "gid_t", private, "<sys/types.h>", public ] }, { symbol: [ "gid_t", private, "<grp.h>", public ] }, { symbol: [ "gid_t", private, "<pwd.h>", public ] }, + { symbol: [ "gid_t", private, "<signal.h>", public ] }, { symbol: [ "gid_t", private, "<stropts.h>", public ] }, { symbol: [ "gid_t", private, "<sys/ipc.h>", public ] }, { symbol: [ "gid_t", private, "<sys/stat.h>", public ] }, { symbol: [ "gid_t", private, "<unistd.h>", public ] }, { symbol: [ "id_t", private, "<sys/types.h>", public ] }, { symbol: [ "id_t", private, "<sys/resource.h>", public ] }, + { symbol: [ "imaxdiv_t", private, "<inttypes.h>", public ] }, + { symbol: [ "intmax_t", private, "<stdint.h>", public ] }, + { symbol: [ "uintmax_t", private, "<stdint.h>", public ] }, { symbol: [ "ino64_t", private, "<sys/types.h>", public ] }, { symbol: [ "ino64_t", private, "<dirent.h>", public ] }, { symbol: [ "ino_t", private, "<sys/types.h>", public ] }, { symbol: [ "ino_t", private, "<dirent.h>", public ] }, { symbol: [ "ino_t", private, "<sys/stat.h>", public ] }, - { symbol: [ "int8_t", private, "<sys/types.h>", public ] }, { symbol: [ "int8_t", private, "<stdint.h>", public ] }, { symbol: [ "int16_t", private, "<stdint.h>", public ] }, { symbol: [ "int32_t", private, "<stdint.h>", public ] }, @@ -54,69 +71,138 @@ { symbol: [ "uint64_t", private, "<stdint.h>", public ] }, { symbol: [ "intptr_t", private, "<stdint.h>", public ] }, { symbol: [ "uintptr_t", private, "<stdint.h>", public ] }, - { symbol: [ "intptr_t", private, "<unistd.h>", public ] }, { symbol: [ "key_t", private, "<sys/types.h>", public ] }, { symbol: [ "key_t", private, "<sys/ipc.h>", public ] }, + { symbol: [ "lconv", private, "<locale.h>", public ] }, + { symbol: [ "ldiv_t", private, "<stdlib.h>", public ] }, + { symbol: [ "lldiv_t", private, "<stdlib.h>", public ] }, { symbol: [ "max_align_t", private, "<stddef.h>", public ] }, { symbol: [ "mode_t", private, "<sys/types.h>", public ] }, - { symbol: [ "mode_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "mode_t", private, "<fcntl.h>", public ] }, + { symbol: [ "mode_t", private, "<ndbm.h>", public ] }, + { symbol: [ "mode_t", private, "<spawn.h>", public ] }, { symbol: [ "mode_t", private, "<sys/ipc.h>", public ] }, { symbol: [ "mode_t", private, "<sys/mman.h>", public ] }, + { symbol: [ "mode_t", private, "<sys/stat.h>", public ] }, { symbol: [ "nlink_t", private, "<sys/types.h>", public ] }, { symbol: [ "nlink_t", private, "<sys/stat.h>", public ] }, { symbol: [ "off64_t", private, "<sys/types.h>", public ] }, { symbol: [ "off64_t", private, "<unistd.h>", public ] }, { symbol: [ "off_t", private, "<sys/types.h>", public ] }, - { symbol: [ "off_t", private, "<unistd.h>", public ] }, - { symbol: [ "off_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "off_t", private, "<aio.h>", public ] }, + { symbol: [ "off_t", private, "<fcntl.h>", public ] }, + { symbol: [ "off_t", private, "<stdio.h>", public ] }, { symbol: [ "off_t", private, "<sys/mman.h>", public ] }, + { symbol: [ "off_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "off_t", private, "<unistd.h>", public ] }, { symbol: [ "pid_t", private, "<sys/types.h>", public ] }, - { symbol: [ "pid_t", private, "<unistd.h>", public ] }, + { symbol: [ "pid_t", private, "<fcntl.h>", public ] }, + { symbol: [ "pid_t", private, "<sched.h>", public ] }, { symbol: [ "pid_t", private, "<signal.h>", public ] }, + { symbol: [ "pid_t", private, "<spawn.h>", public ] }, { symbol: [ "pid_t", private, "<sys/msg.h>", public ] }, + { symbol: [ "pid_t", private, "<sys/sem.h>", public ] }, { symbol: [ "pid_t", private, "<sys/shm.h>", public ] }, + { symbol: [ "pid_t", private, "<sys/wait.h>", public ] }, { symbol: [ "pid_t", private, "<termios.h>", public ] }, { symbol: [ "pid_t", private, "<time.h>", public ] }, + { symbol: [ "pid_t", private, "<unistd.h>", public ] }, { symbol: [ "pid_t", private, "<utmpx.h>", public ] }, { symbol: [ "ptrdiff_t", private, "<stddef.h>", public ] }, + { symbol: [ "regex_t", private, "<regex.h>", public ] }, + { symbol: [ "regmatch_t", private, "<regex.h>", public ] }, + { symbol: [ "regoff_t", private, "<regex.h>", public ] }, + { symbol: [ "sigevent", private, "<signal.h>", public ] }, + { symbol: [ "sigevent", private, "<aio.h>", public ] }, + { symbol: [ "sigevent", private, "<mqueue.h>", public ] }, + { symbol: [ "sigevent", private, "<time.h>", public ] }, + { symbol: [ "siginfo_t", private, "<signal.h>", public ] }, + { symbol: [ "siginfo_t", private, "<sys/wait.h>", public ] }, { symbol: [ "sigset_t", private, "<signal.h>", public ] }, - { symbol: [ "sigset_t", private, "<sys/epoll.h>", public ] }, + { symbol: [ "sigset_t", private, "<spawn.h>", public ] }, { symbol: [ "sigset_t", private, "<sys/select.h>", public ] }, - { symbol: [ "socklen_t", private, "<bits/socket.h>", private ] }, - { symbol: [ "socklen_t", private, "<unistd.h>", public ] }, - { symbol: [ "socklen_t", private, "<arpa/inet.h>", public ] }, + { symbol: [ "sigval", private, "<signal.h>", public ] }, + { symbol: [ "sockaddr", private, "<sys/socket.h>", public ] }, + { symbol: [ "socklen_t", private, "<sys/socket.h>", public ] }, + { symbol: [ "socklen_t", private, "<netdb.h>", public ] }, { symbol: [ "ssize_t", private, "<sys/types.h>", public ] }, - { symbol: [ "ssize_t", private, "<unistd.h>", public ] }, + { symbol: [ "ssize_t", private, "<aio.h>", public ] }, { symbol: [ "ssize_t", private, "<monetary.h>", public ] }, + { symbol: [ "ssize_t", private, "<mqueue.h>", public ] }, + { symbol: [ "ssize_t", private, "<stdio.h>", public ] }, { symbol: [ "ssize_t", private, "<sys/msg.h>", public ] }, + { symbol: [ "ssize_t", private, "<sys/socket.h>", public ] }, + { symbol: [ "ssize_t", private, "<sys/uio.h>", public ] }, + { symbol: [ "ssize_t", private, "<unistd.h>", public ] }, + { symbol: [ "stat", private, "<sys/stat.h>", public ] }, + { symbol: [ "stat", private, "<ftw.h>", public ] }, { symbol: [ "suseconds_t", private, "<sys/types.h>", public ] }, - { symbol: [ "suseconds_t", private, "<sys/time.h>", public ] }, { symbol: [ "suseconds_t", private, "<sys/select.h>", public ] }, - { symbol: [ "time_t", private, "<sys/types.h>", public ] }, + { symbol: [ "suseconds_t", private, "<sys/time.h>", public ] }, { symbol: [ "time_t", private, "<time.h>", public ] }, + { symbol: [ "time_t", private, "<sched.h>", public ] }, + { symbol: [ "time_t", private, "<sys/msg.h>", public ] }, + { symbol: [ "time_t", private, "<sys/select.h>", public ] }, + { symbol: [ "time_t", private, "<sys/sem.h>", public ] }, + { symbol: [ "time_t", private, "<sys/shm.h>", public ] }, + { symbol: [ "time_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "time_t", private, "<sys/time.h>", public ] }, + { symbol: [ "time_t", private, "<sys/types.h>", public ] }, + { symbol: [ "time_t", private, "<utime.h>", public ] }, + { symbol: [ "timer_t", private, "<sys/types.h>", public ] }, + { symbol: [ "timer_t", private, "<time.h>", public ] }, { symbol: [ "timespec", private, "<time.h>", public ] }, + { symbol: [ "timespec", private, "<aio.h>", public ] }, + { symbol: [ "timespec", private, "<mqueue.h>", public ] }, + { symbol: [ "timespec", private, "<sched.h>", public ] }, + { symbol: [ "timespec", private, "<signal.h>", public ] }, + { symbol: [ "timespec", private, "<sys/select.h>", public ] }, + { symbol: [ "timespec", private, "<sys/stat.h>", public ] }, { symbol: [ "timeval", private, "<sys/time.h>", public ] }, + { symbol: [ "timeval", private, "<sys/resource.h>", public ] }, + { symbol: [ "timeval", private, "<sys/select.h>", public ] }, + { symbol: [ "timeval", private, "<utmpx.h>", public ] }, { symbol: [ "u_char", private, "<sys/types.h>", public ] }, { symbol: [ "u_char", private, "<rpc/types.h>", public ] }, { symbol: [ "uid_t", private, "<sys/types.h>", public ] }, - { symbol: [ "uid_t", private, "<unistd.h>", public ] }, { symbol: [ "uid_t", private, "<pwd.h>", public ] }, { symbol: [ "uid_t", private, "<signal.h>", public ] }, { symbol: [ "uid_t", private, "<stropts.h>", public ] }, { symbol: [ "uid_t", private, "<sys/ipc.h>", public ] }, { symbol: [ "uid_t", private, "<sys/stat.h>", public ] }, + { symbol: [ "uid_t", private, "<unistd.h>", public ] }, { symbol: [ "useconds_t", private, "<sys/types.h>", public ] }, { symbol: [ "useconds_t", private, "<unistd.h>", public ] }, { symbol: [ "wchar_t", private, "<stddef.h>", public ] }, { symbol: [ "wchar_t", private, "<stdlib.h>", public ] }, - # glob.h seems to define size_t if necessary, but it should come from stddef. { symbol: [ "size_t", private, "<stddef.h>", public ] }, + { symbol: [ "size_t", private, "<aio.h>", public ] }, + { symbol: [ "size_t", private, "<glob.h>", public ] }, + { symbol: [ "size_t", private, "<grp.h>", public ] }, + { symbol: [ "size_t", private, "<iconv.h>", public ] }, + { symbol: [ "size_t", private, "<monetary.h>", public ] }, + { symbol: [ "size_t", private, "<mqueue.h>", public ] }, + { symbol: [ "size_t", private, "<ndbm.h>", public ] }, + { symbol: [ "size_t", private, "<pwd.h>", public ] }, + { symbol: [ "size_t", private, "<regex.h>", public ] }, + { symbol: [ "size_t", private, "<search.h>", public ] }, + { symbol: [ "size_t", private, "<signal.h>", public ] }, { symbol: [ "size_t", private, "<stdio.h>", public ] }, { symbol: [ "size_t", private, "<stdlib.h>", public ] }, { symbol: [ "size_t", private, "<string.h>", public ] }, + { symbol: [ "size_t", private, "<strings.h>", public ] }, + { symbol: [ "size_t", private, "<sys/mman.h>", public ] }, + { symbol: [ "size_t", private, "<sys/msg.h>", public ] }, + { symbol: [ "size_t", private, "<sys/sem.h>", public ] }, + { symbol: [ "size_t", private, "<sys/shm.h>", public ] }, + { symbol: [ "size_t", private, "<sys/socket.h>", public ] }, + { symbol: [ "size_t", private, "<sys/types.h>", public ] }, + { symbol: [ "size_t", private, "<sys/uio.h>", public ] }, { symbol: [ "size_t", private, "<time.h>", public ] }, { symbol: [ "size_t", private, "<uchar.h>", public ] }, + { symbol: [ "size_t", private, "<unistd.h>", public ] }, { symbol: [ "size_t", private, "<wchar.h>", public ] }, + { symbol: [ "size_t", private, "<wordexp.h>", public ] }, # Macros that can be defined in more than one file, don't have the # same __foo_defined guard that other types do, so the grep above # doesn't discover them. Until I figure out a better way, I just @@ -124,7 +210,13 @@ { symbol: [ "EOF", private, "<stdio.h>", public ] }, { symbol: [ "EOF", private, "<libio.h>", public ] }, { symbol: [ "FILE", private, "<stdio.h>", public ] }, + { symbol: [ "MAP_STACK", private, "<sys/mman.h>", public ] }, + { symbol: [ "MAP_STACK", private, "<linux/mman.h>", public ] }, + { symbol: [ "SIGCHLD", private, "<signal.h>", public ] }, + { symbol: [ "SIGCHLD", private, "<linux/signal.h>", public ] }, { symbol: [ "va_list", private, "<stdarg.h>", public ] }, + { symbol: [ "va_list", private, "<stdio.h>", public ] }, + { symbol: [ "va_list", private, "<wchar.h>", public ] }, # These are symbols that could be defined in either stdlib.h or # malloc.h, but we always want the stdlib location. { symbol: [ "malloc", private, "<stdlib.h>", public ] }, 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) @@ -167,6 +167,7 @@ using clang::ConstructorUsingShadowDecl; using clang::Decl; using clang::DeclContext; using clang::DeclRefExpr; +using clang::DeducedTemplateSpecializationType; using clang::EnumType; using clang::Expr; using clang::FileEntry; @@ -191,6 +192,7 @@ using clang::QualifiedTypeLoc; using clang::RecordDecl; using clang::RecursiveASTVisitor; using clang::ReferenceType; +using clang::Sema; using clang::SourceLocation; using clang::Stmt; using clang::SubstTemplateTypeParmType; @@ -532,17 +534,10 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> { //------------------------------------------------------------ // (4) Add implicit text. - - // When we see an object that has implicit text that iwyu - // wants to look at, we make callbacks as if that text had - // been explicitly written. Here's text we consider: // - // * CXXDestructorDecl: a destructor call for each non-POD field - // in the dtor's class, and each base type of that class. - // * CXXRecordDecl: a CXXConstructorDecl for each implicit - // constructor (zero-arg and copy). A CXXDestructor decl - // if the destructor is implicit. A CXXOperatorCallDecl if - // operator= is explicit. + // This simulates a call to the destructor of every non-POD field and base + // class in all classes with destructors, to mark them as used by virtue of + // being class members. bool TraverseCXXDestructorDecl(clang::CXXDestructorDecl* decl) { if (!Base::TraverseCXXDestructorDecl(decl)) return false; if (CanIgnoreCurrentASTNode()) return true; @@ -575,69 +570,12 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> { return true; } - // clang lazily constructs the implicit methods of a C++ class (the - // default constructor and destructor, etc) -- it only bothers to - // create a CXXMethodDecl if someone actually calls these classes. - // But we need to be non-lazy: iwyu depends on analyzing what future - // code *may* call in a class, not what current code *does*. So we - // force all the lazy evaluation to happen here. This will - // (possibly) add a bunch of MethodDecls to the AST, as children of - // decl. We're hoping it will always be safe to modify the AST - // while it's being traversed! - void InstantiateImplicitMethods(CXXRecordDecl* decl) { - if (decl->isDependentType()) // only instantiate if class is instantiated - return; - - clang::Sema& sema = compiler()->getSema(); - DeclContext::lookup_result ctors = sema.LookupConstructors(decl); - for (NamedDecl* ctor_lookup : ctors) { - // Ignore templated or inheriting constructors. - if (isa<FunctionTemplateDecl>(ctor_lookup) || - isa<UsingDecl>(ctor_lookup) || - isa<ConstructorUsingShadowDecl>(ctor_lookup)) - continue; - CXXConstructorDecl* ctor = cast<CXXConstructorDecl>(ctor_lookup); - if (!ctor->hasBody() && !ctor->isDeleted() && ctor->isImplicit()) { - if (sema.getSpecialMember(ctor) == clang::Sema::CXXDefaultConstructor) { - sema.DefineImplicitDefaultConstructor(CurrentLoc(), ctor); - } else { - // TODO(nlewycky): enable this! - //sema.DefineImplicitCopyConstructor(CurrentLoc(), ctor); - } - } - // Unreferenced template constructors stay uninstantiated on purpose. - } - - if (CXXDestructorDecl* dtor = sema.LookupDestructor(decl)) { - if (!dtor->isDeleted()) { - if (!dtor->hasBody() && dtor->isImplicit()) - sema.DefineImplicitDestructor(CurrentLoc(), dtor); - if (!dtor->isDefined() && dtor->getTemplateInstantiationPattern()) - sema.PendingInstantiations.emplace_back(dtor, CurrentLoc()); - } - } - - // TODO(nlewycky): copy assignment operator - - // clang queues up method instantiations. We need to process them now. - sema.PerformPendingInstantiations(); - } - - // Handle implicit methods that otherwise wouldn't be seen by RAV. - bool TraverseCXXRecordDecl(clang::CXXRecordDecl* decl) { - if (CanIgnoreCurrentASTNode()) return true; - // We only care about classes that are actually defined. - if (decl && decl->isThisDeclarationADefinition()) { - InstantiateImplicitMethods(decl); - } - - return Base::TraverseCXXRecordDecl(decl); - } - + // Class template specialization are similar to regular C++ classes, + // particularly they need the same custom handling of implicit destructors. bool TraverseClassTemplateSpecializationDecl( clang::ClassTemplateSpecializationDecl* decl) { if (!Base::TraverseClassTemplateSpecializationDecl(decl)) return false; - return TraverseCXXRecordDecl(decl); + return Base::TraverseCXXRecordDecl(decl); } //------------------------------------------------------------ @@ -1322,7 +1260,26 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { for (const NamedDecl* redecl : GetClassRedecls(decl)) { if (GetFileEntry(redecl) == macro_def_file && IsForwardDecl(redecl)) { fwd_decl = redecl; + break; + } + } + + if (!fwd_decl) { + if (const auto* func_decl = dyn_cast<FunctionDecl>(decl)) { + if (const FunctionTemplateDecl* ft_decl = + func_decl->getPrimaryTemplate()) { + VERRS(5) << "No fwd-decl found, looking for function template decl\n"; + for (const NamedDecl* redecl : ft_decl->redecls()) { + if (GetFileEntry(redecl) == macro_def_file) { + fwd_decl = redecl; + break; + } + } + } + } + } + if (fwd_decl) { // Make sure we keep that forward-declaration, even if it's probably // unused in this file. IwyuFileInfo* file_info = @@ -1330,8 +1287,6 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { file_info->ReportForwardDeclareUse( spelling_loc, fwd_decl, ComputeUseFlags(current_ast_node()), nullptr); - break; - } } // Resolve the best use location based on our current knowledge. @@ -1390,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>). @@ -1460,12 +1424,12 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { } set<const Type*> GetCallerResponsibleTypesForTypedef( - const TypedefDecl* decl) { + const TypedefNameDecl* decl) { set<const Type*> retval; const Type* underlying_type = decl->getUnderlyingType().getTypePtr(); // If the underlying type is itself a typedef, we recurse. if (const TypedefType* underlying_typedef = DynCastFrom(underlying_type)) { - if (const TypedefDecl* underlying_typedef_decl + if (const TypedefNameDecl* underlying_typedef_decl = DynCastFrom(TypeToDeclAsWritten(underlying_typedef))) { // TODO(csilvers): if one of the intermediate typedefs // #includes the necessary definition of the 'final' @@ -1610,12 +1574,12 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { // anywhere. ('autocast' is similar, but is handled in // VisitCastExpr; 'fn-return-type' is also similar and is // handled in HandleFunctionCall.) - if (const TypedefDecl* typedef_decl = DynCastFrom(target_decl)) { + if (const TypedefNameDecl* typedef_decl = DynCastFrom(target_decl)) { // One exception: if this TypedefType is being used in another // typedef (that is, 'typedef MyTypedef OtherTypdef'), then the // user -- the other typedef -- is never responsible for the // underlying type. Instead, users of that typedef are. - if (!current_ast_node()->template ParentIsA<TypedefDecl>()) { + if (!current_ast_node()->template ParentIsA<TypedefNameDecl>()) { const set<const Type*>& underlying_types = GetCallerResponsibleTypesForTypedef(typedef_decl); if (!underlying_types.empty()) { @@ -1757,7 +1721,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { // iwyu will demand the full type of pair, but not of its template // arguments. This is handled not here, but below, in // VisitSubstTemplateTypeParmType. - bool VisitTypedefDecl(clang::TypedefDecl* decl) { + bool VisitTypedefNameDecl(clang::TypedefNameDecl* decl) { if (CanIgnoreCurrentASTNode()) return true; const Type* underlying_type = decl->getUnderlyingType().getTypePtr(); const Type* deref_type @@ -1770,7 +1734,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { current_ast_node()->set_in_forward_declare_context(false); } - return Base::VisitTypedefDecl(decl); + return Base::VisitTypedefNameDecl(decl); } // If we're a declared (not defined) function, all our types -- @@ -1946,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(); } @@ -1999,6 +1963,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { case clang::CK_IntegralToFixedPoint: case clang::CK_IntegralToFloating: case clang::CK_IntegralToPointer: + case clang::CK_MatrixCast: case clang::CK_MemberPointerToBoolean: case clang::CK_NullToMemberPointer: case clang::CK_NullToPointer: @@ -2427,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( @@ -2591,7 +2554,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { // If we're in a typedef, we don't want to forward-declare even if // we're a pointer. ('typedef Foo* Bar; Bar x; x->a' needs full // type of Foo.) - if (ast_node->ParentIsA<TypedefDecl>()) + if (ast_node->ParentIsA<TypedefNameDecl>()) return false; // If we ourselves are a forward-decl -- that is, we're the type @@ -2645,8 +2608,11 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { void AddShadowDeclarations(const UsingDecl* using_decl) { for (const UsingShadowDecl* shadow : using_decl->shadows()) { - visitor_state_->using_declarations.insert( - make_pair(shadow->getTargetDecl(), shadow->getUsingDecl())); + if (const auto* introducer = + dyn_cast<UsingDecl>(shadow->getIntroducer())) { + visitor_state_->using_declarations.insert( + make_pair(shadow->getTargetDecl(), introducer)); + } } } @@ -2665,8 +2631,9 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> { const DeclContext* use_context) { // First, if we have a UsingShadowDecl, then we don't need to do anything // because we can just directly return the using decl from that. - if (const UsingShadowDecl* shadow = DynCastFrom(decl)) - return shadow->getUsingDecl(); + if (const auto* shadow = dyn_cast<UsingShadowDecl>(decl)) { + return dyn_cast<UsingDecl>(shadow->getIntroducer()); + } // But, if we don't have a UsingShadowDecl, then we need to look through // all the using-decls of the given decl. We limit them to ones that are @@ -3656,22 +3623,36 @@ class IwyuAstConsumer const_cast<IwyuPreprocessorInfo*>(&preprocessor_info())-> HandlePreprocessingDone(); + TranslationUnitDecl* tu_decl = context.getTranslationUnitDecl(); + + // Sema::TUScope is reset after parsing, but Sema::getCurScope still points + // to the translation unit decl scope. TUScope is required for lookup in + // some complex scenarios, so re-wire it before running IWYU AST passes. + // Assert their expected state so we notice if these assumptions change. + Sema& sema = compiler()->getSema(); + CHECK_(sema.TUScope == nullptr); + CHECK_(sema.getCurScope() != nullptr); + sema.TUScope = sema.getCurScope(); + // We run a separate pass to force parsing of late-parsed function // templates. - ParseFunctionTemplates(context.getTranslationUnitDecl()); + ParseFunctionTemplates(sema, tu_decl); + + // Clang lazily constructs the implicit methods of a C++ class (the + // default constructor and destructor, etc) -- it only bothers to + // create a CXXMethodDecl if someone actually calls these classes. + // But we need to be non-lazy: IWYU depends on analyzing what future + // code *may* call in a class, not what current code *does*. So we + // force all the lazy evaluation to happen here. + InstantiateImplicitMethods(sema, tu_decl); - TraverseDecl(context.getTranslationUnitDecl()); + // Run IWYU analysis. + TraverseDecl(tu_decl); // 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(); @@ -3701,13 +3682,20 @@ 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(TranslationUnitDecl* decl) { - set<FunctionDecl*> late_parsed_decls = GetLateParsedFunctionDecls(decl); - clang::Sema& sema = compiler()->getSema(); + void ParseFunctionTemplates(Sema& sema, TranslationUnitDecl* tu_decl) { + set<FunctionDecl*> late_parsed_decls = GetLateParsedFunctionDecls(tu_decl); // If we have any late-parsed functions, make sure the // -fdelayed-template-parsing flag is on. Otherwise we don't know where @@ -3730,6 +3718,55 @@ class IwyuAstConsumer } } + void InstantiateImplicitMethods(Sema& sema, TranslationUnitDecl* tu_decl) { + // Collect all implicit ctors/dtors that need to be instantiated. + struct Visitor : public RecursiveASTVisitor<Visitor> { + Visitor(Sema& sema) : sema(sema) { + } + + bool VisitCXXRecordDecl(CXXRecordDecl* decl) { + if (CanIgnoreLocation(GetLocation(decl))) + return true; + + if (!decl->getDefinition() || decl->isDependentContext() || + decl->isBeingDefined()) { + return true; + } + + if (CXXConstructorDecl* ctor = sema.LookupDefaultConstructor(decl)) { + may_need_definition.insert(ctor); + } else if (CXXDestructorDecl* dtor = sema.LookupDestructor(decl)) { + may_need_definition.insert(dtor); + } + + return true; + } + + Sema& sema; + std::set<CXXMethodDecl*> may_need_definition; + }; + + // Run visitor to collect implicit methods. + Visitor v(sema); + v.TraverseDecl(tu_decl); + + // For each method collected, let Sema define them. + for (CXXMethodDecl* method : v.may_need_definition) { + if (!method->isDefaulted() || method->isDeleted() || method->hasBody()) + continue; + + SourceLocation loc = GetLocation(method->getParent()); + if (auto* ctor = dyn_cast<CXXConstructorDecl>(method)) { + sema.DefineImplicitDefaultConstructor(loc, ctor); + } else if (auto* dtor = dyn_cast<CXXDestructorDecl>(method)) { + sema.DefineImplicitDestructor(loc, dtor); + } + } + + // Clang queues up method instantiations. Process them now. + sema.PerformPendingInstantiations(); + } + //------------------------------------------------------------ // AST visitors. We start by adding a visitor callback for // most of the subclasses of Decl/Stmt/Type listed in: @@ -3913,14 +3950,13 @@ class IwyuAstConsumer // TODO(csilvers): we can probably relax this rule in .cc files. // TODO(csilvers): this should really move into IwyuBaseASTVisitor // (that way we'll correctly identify need for hash<> in hash_set). - // This is a Traverse*() because Visit*() can't call HandleFunctionCall(). - bool TraverseTypedefDecl(clang::TypedefDecl* decl) { - // Before we go up the tree, make sure the parents know we don't - // forward-declare the underlying type of a typedef decl. - current_ast_node()->set_in_forward_declare_context(false); - if (!Base::TraverseTypedefDecl(decl)) - return false; - if (CanIgnoreCurrentASTNode()) return true; + // This is called from Traverse*() because Visit*() + // can't call HandleFunctionCall(). + bool HandleAliasedClassMethods(TypedefNameDecl* decl) { + if (CanIgnoreCurrentASTNode()) + return true; + if (current_ast_node()->in_forward_declare_context()) + return true; const Type* underlying_type = decl->getUnderlyingType().getTypePtr(); const Decl* underlying_decl = TypeToDeclAsWritten(underlying_type); @@ -3947,6 +3983,20 @@ class IwyuAstConsumer return true; } + bool TraverseTypedefDecl(clang::TypedefDecl* decl) { + if (!Base::TraverseTypedefDecl(decl)) + return false; + + return HandleAliasedClassMethods(decl); + } + + bool TraverseTypeAliasDecl(clang::TypeAliasDecl* decl) { + if (!Base::TraverseTypeAliasDecl(decl)) + return false; + + return HandleAliasedClassMethods(decl); + } + // --- Visitors of types derived from clang::Stmt. // Called whenever a variable, function, enum, etc is used. @@ -4002,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; @@ -4063,18 +4127,21 @@ class IwyuAstConsumer bool VisitTemplateName(TemplateName template_name) { if (CanIgnoreCurrentASTNode()) return true; if (!Base::VisitTemplateName(template_name)) return false; - // The only time we can see a TemplateName not in the - // context of a TemplateSpecializationType is when it's - // the default argument of a template template arg: + // We can see TemplateName not in the context of aTemplateSpecializationType + // when it's either the default argument of a template template arg: // template<template<class T> class A = TplNameWithoutTST> class Foo ... - // So that's the only case we need to handle here. + // or a deduced template specialization: + // std::pair x(10, 20); // type of x is really std::pair<int, int> + // So that's the only cases we need to handle here. // TODO(csilvers): check if this is really forward-declarable or // not. You *could* do something like: 'template<template<class // T> class A = Foo> class C { A<int>* x; };' and never // dereference x, but that's pretty unlikely. So for now, we just // assume these default template template args always need full // type info. - if (IsDefaultTemplateTemplateArg(current_ast_node())) { + const ASTNode* ast_node = current_ast_node(); + if (ast_node->ParentIsA<DeducedTemplateSpecializationType>() || + IsDefaultTemplateTemplateArg(ast_node)) { current_ast_node()->set_in_forward_declare_context(false); ReportDeclUse(CurrentLoc(), template_name.getAsTemplateDecl()); } @@ -4140,7 +4207,6 @@ class IwyuAction : public ASTFrontendAction { #include "iwyu_driver.h" #include "clang/Frontend/FrontendAction.h" -#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/TargetSelect.h" using include_what_you_use::OptionsParser; @@ -4148,29 +4214,28 @@ using include_what_you_use::IwyuAction; using include_what_you_use::CreateCompilerInstance; int main(int argc, char **argv) { - // Must initialize X86 target to be able to parse Microsoft inline - // assembly. We do this unconditionally, because it allows an IWYU - // built for non-X86 targets to parse MS inline asm without choking. - LLVMInitializeX86TargetInfo(); - LLVMInitializeX86TargetMC(); - LLVMInitializeX86AsmParser(); + 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(); + llvm::InitializeAllTargetMCs(); + 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 9982145..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; @@ -1095,11 +1102,14 @@ bool DeclsAreInSameClass(const Decl* decl1, const Decl* decl2) { return decl1->getDeclContext()->isRecord(); } -bool IsBuiltinFunction(const clang::NamedDecl* decl, - const std::string& symbol_name) { +bool IsBuiltinFunction(const clang::NamedDecl* decl) { if (const clang::IdentifierInfo* iden = decl->getIdentifier()) { - return iden->getBuiltinID() != 0 && - !clang::Builtin::Context::isBuiltinFunc(symbol_name.c_str()); + unsigned builtin_id = iden->getBuiltinID(); + if (builtin_id != 0) { + const clang::Builtin::Context& ctx = decl->getASTContext().BuiltinInfo; + return !ctx.isPredefinedLibFunction(builtin_id) && + !ctx.isHeaderDependentFunction(builtin_id); + } } return false; } @@ -1472,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 18ddf2d..f4f81a0 100644 --- a/iwyu_ast_util.h +++ b/iwyu_ast_util.h @@ -652,8 +652,7 @@ const clang::NamedDecl* GetNonfriendClassRedecl(const clang::NamedDecl* decl); bool DeclsAreInSameClass(const clang::Decl* decl1, const clang::Decl* decl2); // Returns true if the given decl/name is a builtin function -bool IsBuiltinFunction(const clang::NamedDecl* decl, - const std::string& symbol_name); +bool IsBuiltinFunction(const clang::NamedDecl* decl); // --- Utilities for Type. @@ -817,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 36ac3ae..8ba6c05 100644 --- a/iwyu_globals.cc +++ b/iwyu_globals.cc @@ -91,11 +91,16 @@ static void PrintHelp(const char* extra_msg) { " the maximum line length can still be exceeded with long\n" " file names (default: 80).\n" " --no_comments: do not add 'why' comments.\n" + " --update_comments: always add 'why' comments, even if no\n" + " #include lines need to be added or removed.\n" " --no_fwd_decls: do not use forward declarations.\n" " --verbose=<level>: the higher the level, the more output.\n" " --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" @@ -116,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 @@ -163,9 +184,12 @@ CommandlineFlags::CommandlineFlags() prefix_header_include_policy(CommandlineFlags::kAdd), pch_in_code(false), no_comments(false), + 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"); } @@ -182,9 +206,12 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) { {"pch_in_code", no_argument, nullptr, 'h'}, {"max_line_length", required_argument, nullptr, 'l'}, {"no_comments", no_argument, nullptr, 'o'}, + {"update_comments", no_argument, nullptr, 'u'}, {"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"; @@ -197,6 +224,7 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) { case 'm': mapping_files.push_back(optarg); break; case 'n': no_default_mappings = true; break; case 'o': no_comments = true; break; + case 'u': update_comments = true; break; case 'f': no_fwd_decls = true; break; case 'x': if (strcmp(optarg, "add") == 0) { @@ -207,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; @@ -217,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 @@ -246,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 242cdf6..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; @@ -98,9 +89,12 @@ struct CommandlineFlags { PrefixHeaderIncludePolicy prefix_header_include_policy; bool pch_in_code; // Treat the first seen include as a PCH. No short option. bool no_comments; // Disable 'why' comments. No short option. + bool update_comments; // Force 'why' comments. No short option. 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_include_picker.cc b/iwyu_include_picker.cc index f5d2dbc..26b55d8 100644 --- a/iwyu_include_picker.cc +++ b/iwyu_include_picker.cc @@ -16,6 +16,7 @@ // not hash_map: it's not as portable and needs hash<string>. #include <map> // for map, map<>::mapped_type, etc #include <memory> +#include <regex> #include <string> // for string, basic_string, etc #include <system_error> // for error_code #include <utility> // for pair, make_pair @@ -33,7 +34,6 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/Regex.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/YAMLParser.h" #include "clang/Basic/FileManager.h" @@ -89,19 +89,33 @@ const IncludeMapEntry libc_symbol_map[] = { // an option for this type. That's the preferred #include all else // equal. The visibility on the symbol-name is ignored; by convention // we always set it to kPrivate. - { "blksize_t", kPrivate, "<sys/types.h>", kPublic }, - { "blkcnt_t", kPrivate, "<sys/stat.h>", kPublic }, + { "aiocb", kPrivate, "<aio.h>", kPublic }, { "blkcnt_t", kPrivate, "<sys/types.h>", kPublic }, + { "blkcnt_t", kPrivate, "<sys/stat.h>", kPublic }, + { "blksize_t", kPrivate, "<sys/types.h>", kPublic }, { "blksize_t", kPrivate, "<sys/stat.h>", kPublic }, + { "cc_t", kPrivate, "<termios.h>", kPublic }, { "clock_t", kPrivate, "<sys/types.h>", kPublic }, + { "clock_t", kPrivate, "<sys/time.h>", kPublic }, { "clock_t", kPrivate, "<time.h>", kPublic }, + { "clockid_t", kPrivate, "<sys/types.h>", kPublic }, + { "clockid_t", kPrivate, "<time.h>", kPublic }, { "daddr_t", kPrivate, "<sys/types.h>", kPublic }, { "daddr_t", kPrivate, "<rpc/types.h>", kPublic }, { "dev_t", kPrivate, "<sys/types.h>", kPublic }, { "dev_t", kPrivate, "<sys/stat.h>", kPublic }, + { "div_t", kPrivate, "<stdlib.h>", kPublic }, + { "double_t", kPrivate, "<math.h>", kPublic }, { "error_t", kPrivate, "<errno.h>", kPublic }, { "error_t", kPrivate, "<argp.h>", kPublic }, { "error_t", kPrivate, "<argz.h>", kPublic }, + { "fd_set", kPrivate, "<sys/select.h>", kPublic }, + { "fd_set", kPrivate, "<sys/time.h>", kPublic }, + { "fenv_t", kPrivate, "<fenv.h>", kPublic }, + { "fexcept_t", kPrivate, "<fenv.h>", kPublic }, + { "FILE", kPrivate, "<stdio.h>", kPublic }, + { "FILE", kPrivate, "<wchar.h>", kPublic }, + { "float_t", kPrivate, "<math.h>", kPublic }, { "fsblkcnt_t", kPrivate, "<sys/types.h>", kPublic }, { "fsblkcnt_t", kPrivate, "<sys/statvfs.h>", kPublic }, { "fsfilcnt_t", kPrivate, "<sys/types.h>", kPublic }, @@ -109,18 +123,21 @@ const IncludeMapEntry libc_symbol_map[] = { { "gid_t", kPrivate, "<sys/types.h>", kPublic }, { "gid_t", kPrivate, "<grp.h>", kPublic }, { "gid_t", kPrivate, "<pwd.h>", kPublic }, + { "gid_t", kPrivate, "<signal.h>", kPublic }, { "gid_t", kPrivate, "<stropts.h>", kPublic }, { "gid_t", kPrivate, "<sys/ipc.h>", kPublic }, { "gid_t", kPrivate, "<sys/stat.h>", kPublic }, { "gid_t", kPrivate, "<unistd.h>", kPublic }, { "id_t", kPrivate, "<sys/types.h>", kPublic }, { "id_t", kPrivate, "<sys/resource.h>", kPublic }, + { "imaxdiv_t", kPrivate, "<inttypes.h>", kPublic }, + { "intmax_t", kPrivate, "<stdint.h>", kPublic }, + { "uintmax_t", kPrivate, "<stdint.h>", kPublic }, { "ino64_t", kPrivate, "<sys/types.h>", kPublic }, { "ino64_t", kPrivate, "<dirent.h>", kPublic }, { "ino_t", kPrivate, "<sys/types.h>", kPublic }, { "ino_t", kPrivate, "<dirent.h>", kPublic }, { "ino_t", kPrivate, "<sys/stat.h>", kPublic }, - { "int8_t", kPrivate, "<sys/types.h>", kPublic }, { "int8_t", kPrivate, "<stdint.h>", kPublic }, { "int16_t", kPrivate, "<stdint.h>", kPublic }, { "int32_t", kPrivate, "<stdint.h>", kPublic }, @@ -131,71 +148,140 @@ const IncludeMapEntry libc_symbol_map[] = { { "uint64_t", kPrivate, "<stdint.h>", kPublic }, { "intptr_t", kPrivate, "<stdint.h>", kPublic }, { "uintptr_t", kPrivate, "<stdint.h>", kPublic }, - { "intptr_t", kPrivate, "<unistd.h>", kPublic }, { "key_t", kPrivate, "<sys/types.h>", kPublic }, { "key_t", kPrivate, "<sys/ipc.h>", kPublic }, + { "lconv", kPrivate, "<locale.h>", kPublic }, + { "ldiv_t", kPrivate, "<stdlib.h>", kPublic }, + { "lldiv_t", kPrivate, "<stdlib.h>", kPublic }, { "max_align_t", kPrivate, "<stddef.h>", kPublic }, { "mode_t", kPrivate, "<sys/types.h>", kPublic }, - { "mode_t", kPrivate, "<sys/stat.h>", kPublic }, + { "mode_t", kPrivate, "<fcntl.h>", kPublic }, + { "mode_t", kPrivate, "<ndbm.h>", kPublic }, + { "mode_t", kPrivate, "<spawn.h>", kPublic }, { "mode_t", kPrivate, "<sys/ipc.h>", kPublic }, { "mode_t", kPrivate, "<sys/mman.h>", kPublic }, + { "mode_t", kPrivate, "<sys/stat.h>", kPublic }, { "nlink_t", kPrivate, "<sys/types.h>", kPublic }, { "nlink_t", kPrivate, "<sys/stat.h>", kPublic }, { "off64_t", kPrivate, "<sys/types.h>", kPublic }, { "off64_t", kPrivate, "<unistd.h>", kPublic }, { "off_t", kPrivate, "<sys/types.h>", kPublic }, - { "off_t", kPrivate, "<unistd.h>", kPublic }, - { "off_t", kPrivate, "<sys/stat.h>", kPublic }, + { "off_t", kPrivate, "<aio.h>", kPublic }, + { "off_t", kPrivate, "<fcntl.h>", kPublic }, + { "off_t", kPrivate, "<stdio.h>", kPublic }, { "off_t", kPrivate, "<sys/mman.h>", kPublic }, + { "off_t", kPrivate, "<sys/stat.h>", kPublic }, + { "off_t", kPrivate, "<unistd.h>", kPublic }, { "pid_t", kPrivate, "<sys/types.h>", kPublic }, - { "pid_t", kPrivate, "<unistd.h>", kPublic }, + { "pid_t", kPrivate, "<fcntl.h>", kPublic }, + { "pid_t", kPrivate, "<sched.h>", kPublic }, { "pid_t", kPrivate, "<signal.h>", kPublic }, + { "pid_t", kPrivate, "<spawn.h>", kPublic }, { "pid_t", kPrivate, "<sys/msg.h>", kPublic }, + { "pid_t", kPrivate, "<sys/sem.h>", kPublic }, { "pid_t", kPrivate, "<sys/shm.h>", kPublic }, + { "pid_t", kPrivate, "<sys/wait.h>", kPublic }, { "pid_t", kPrivate, "<termios.h>", kPublic }, { "pid_t", kPrivate, "<time.h>", kPublic }, + { "pid_t", kPrivate, "<unistd.h>", kPublic }, { "pid_t", kPrivate, "<utmpx.h>", kPublic }, { "ptrdiff_t", kPrivate, "<stddef.h>", kPublic }, + { "regex_t", kPrivate, "<regex.h>", kPublic }, + { "regmatch_t", kPrivate, "<regex.h>", kPublic }, + { "regoff_t", kPrivate, "<regex.h>", kPublic }, + { "sigevent", kPrivate, "<signal.h>", kPublic }, + { "sigevent", kPrivate, "<aio.h>", kPublic }, + { "sigevent", kPrivate, "<mqueue.h>", kPublic }, + { "sigevent", kPrivate, "<time.h>", kPublic }, + { "siginfo_t", kPrivate, "<signal.h>", kPublic }, + { "siginfo_t", kPrivate, "<sys/wait.h>", kPublic }, { "sigset_t", kPrivate, "<signal.h>", kPublic }, - { "sigset_t", kPrivate, "<sys/epoll.h>", kPublic }, + { "sigset_t", kPrivate, "<spawn.h>", kPublic }, { "sigset_t", kPrivate, "<sys/select.h>", kPublic }, - { "socklen_t", kPrivate, "<bits/socket.h>", kPrivate }, - { "socklen_t", kPrivate, "<unistd.h>", kPublic }, - { "socklen_t", kPrivate, "<arpa/inet.h>", kPublic }, + { "sigval", kPrivate, "<signal.h>", kPublic }, + { "sockaddr", kPrivate, "<sys/socket.h>", kPublic }, + { "socklen_t", kPrivate, "<sys/socket.h>", kPublic }, + { "socklen_t", kPrivate, "<netdb.h>", kPublic }, { "ssize_t", kPrivate, "<sys/types.h>", kPublic }, - { "ssize_t", kPrivate, "<unistd.h>", kPublic }, + { "ssize_t", kPrivate, "<aio.h>", kPublic }, { "ssize_t", kPrivate, "<monetary.h>", kPublic }, + { "ssize_t", kPrivate, "<mqueue.h>", kPublic }, + { "ssize_t", kPrivate, "<stdio.h>", kPublic }, { "ssize_t", kPrivate, "<sys/msg.h>", kPublic }, + { "ssize_t", kPrivate, "<sys/socket.h>", kPublic }, + { "ssize_t", kPrivate, "<sys/uio.h>", kPublic }, + { "ssize_t", kPrivate, "<unistd.h>", kPublic }, + { "stat", kPrivate, "<sys/stat.h>", kPublic }, + { "stat", kPrivate, "<ftw.h>", kPublic }, { "suseconds_t", kPrivate, "<sys/types.h>", kPublic }, - { "suseconds_t", kPrivate, "<sys/time.h>", kPublic }, { "suseconds_t", kPrivate, "<sys/select.h>", kPublic }, - { "time_t", kPrivate, "<sys/types.h>", kPublic }, + { "suseconds_t", kPrivate, "<sys/time.h>", kPublic }, { "time_t", kPrivate, "<time.h>", kPublic }, + { "time_t", kPrivate, "<sched.h>", kPublic }, + { "time_t", kPrivate, "<sys/msg.h>", kPublic }, + { "time_t", kPrivate, "<sys/select.h>", kPublic }, + { "time_t", kPrivate, "<sys/sem.h>", kPublic }, + { "time_t", kPrivate, "<sys/shm.h>", kPublic }, + { "time_t", kPrivate, "<sys/stat.h>", kPublic }, + { "time_t", kPrivate, "<sys/time.h>", kPublic }, + { "time_t", kPrivate, "<sys/types.h>", kPublic }, + { "time_t", kPrivate, "<utime.h>", kPublic }, + { "timer_t", kPrivate, "<sys/types.h>", kPublic }, + { "timer_t", kPrivate, "<time.h>", kPublic }, { "timespec", kPrivate, "<time.h>", kPublic }, + { "timespec", kPrivate, "<aio.h>", kPublic }, + { "timespec", kPrivate, "<mqueue.h>", kPublic }, + { "timespec", kPrivate, "<sched.h>", kPublic }, + { "timespec", kPrivate, "<signal.h>", kPublic }, + { "timespec", kPrivate, "<sys/select.h>", kPublic }, + { "timespec", kPrivate, "<sys/stat.h>", kPublic }, { "timeval", kPrivate, "<sys/time.h>", kPublic }, + { "timeval", kPrivate, "<sys/resource.h>", kPublic }, + { "timeval", kPrivate, "<sys/select.h>", kPublic }, + { "timeval", kPrivate, "<utmpx.h>", kPublic }, { "u_char", kPrivate, "<sys/types.h>", kPublic }, { "u_char", kPrivate, "<rpc/types.h>", kPublic }, { "uid_t", kPrivate, "<sys/types.h>", kPublic }, - { "uid_t", kPrivate, "<unistd.h>", kPublic }, { "uid_t", kPrivate, "<pwd.h>", kPublic }, { "uid_t", kPrivate, "<signal.h>", kPublic }, { "uid_t", kPrivate, "<stropts.h>", kPublic }, { "uid_t", kPrivate, "<sys/ipc.h>", kPublic }, { "uid_t", kPrivate, "<sys/stat.h>", kPublic }, + { "uid_t", kPrivate, "<unistd.h>", kPublic }, { "useconds_t", kPrivate, "<sys/types.h>", kPublic }, { "useconds_t", kPrivate, "<unistd.h>", kPublic }, { "wchar_t", kPrivate, "<stddef.h>", kPublic }, { "wchar_t", kPrivate, "<stdlib.h>", kPublic }, - // glob.h seems to define size_t if necessary, but it should come from stddef. // It is unspecified if the cname headers provide ::size_t. // <locale.h> is the one header which defines NULL but not size_t. { "size_t", kPrivate, "<stddef.h>", kPublic }, // 'canonical' location for size_t + { "size_t", kPrivate, "<aio.h>", kPublic }, + { "size_t", kPrivate, "<glob.h>", kPublic }, + { "size_t", kPrivate, "<grp.h>", kPublic }, + { "size_t", kPrivate, "<iconv.h>", kPublic }, + { "size_t", kPrivate, "<monetary.h>", kPublic }, + { "size_t", kPrivate, "<mqueue.h>", kPublic }, + { "size_t", kPrivate, "<ndbm.h>", kPublic }, + { "size_t", kPrivate, "<pwd.h>", kPublic }, + { "size_t", kPrivate, "<regex.h>", kPublic }, + { "size_t", kPrivate, "<search.h>", kPublic }, + { "size_t", kPrivate, "<signal.h>", kPublic }, { "size_t", kPrivate, "<stdio.h>", kPublic }, { "size_t", kPrivate, "<stdlib.h>", kPublic }, { "size_t", kPrivate, "<string.h>", kPublic }, + { "size_t", kPrivate, "<strings.h>", kPublic }, + { "size_t", kPrivate, "<sys/mman.h>", kPublic }, + { "size_t", kPrivate, "<sys/msg.h>", kPublic }, + { "size_t", kPrivate, "<sys/sem.h>", kPublic }, + { "size_t", kPrivate, "<sys/shm.h>", kPublic }, + { "size_t", kPrivate, "<sys/socket.h>", kPublic }, + { "size_t", kPrivate, "<sys/types.h>", kPublic }, + { "size_t", kPrivate, "<sys/uio.h>", kPublic }, { "size_t", kPrivate, "<time.h>", kPublic }, { "size_t", kPrivate, "<uchar.h>", kPublic }, + { "size_t", kPrivate, "<unistd.h>", kPublic }, { "size_t", kPrivate, "<wchar.h>", kPublic }, + { "size_t", kPrivate, "<wordexp.h>", kPublic }, // Macros that can be defined in more than one file, don't have the // same __foo_defined guard that other types do, so the grep above // doesn't discover them. Until I figure out a better way, I just @@ -203,7 +289,13 @@ const IncludeMapEntry libc_symbol_map[] = { { "EOF", kPrivate, "<stdio.h>", kPublic }, { "EOF", kPrivate, "<libio.h>", kPublic }, { "FILE", kPrivate, "<stdio.h>", kPublic }, + { "MAP_STACK", kPrivate, "<sys/mman.h>", kPublic }, + { "MAP_STACK", kPrivate, "<linux/mman.h>", kPublic }, + { "SIGCHLD", kPrivate, "<signal.h>", kPublic }, + { "SIGCHLD", kPrivate, "<linux/signal.h>", kPublic }, { "va_list", kPrivate, "<stdarg.h>", kPublic }, + { "va_list", kPrivate, "<stdio.h>", kPublic }, + { "va_list", kPrivate, "<wchar.h>", kPublic }, // These are symbols that could be defined in either stdlib.h or // malloc.h, but we always want the stdlib location. { "malloc", kPrivate, "<stdlib.h>", kPublic }, @@ -262,8 +354,8 @@ const IncludeMapEntry libstdcpp_symbol_map[] = { { "std::size_t", kPrivate, "<cwchar>", kPublic }, }; -// Private -> public include mappings for GNU libc const IncludeMapEntry libc_include_map[] = { + // Private -> public include mappings for GNU libc // ( cd /usr/include && grep '^ *# *include' {sys/,net/,}* | perl -nle 'm/^([^:]+).*<([^>]+)>/ && print qq@ { "<$2>", kPrivate, "<$1>", kPublic },@' | grep bits/ | sort ) // When I saw more than one mapping for these, I typically picked // what I thought was the "best" one. @@ -467,6 +559,8 @@ const IncludeMapEntry libc_include_map[] = { { "<linux/limits.h>", kPrivate, "<limits.h>", kPublic }, // PATH_MAX { "<linux/prctl.h>", kPrivate, "<sys/prctl.h>", kPublic }, { "<sys/ucontext.h>", kPrivate, "<ucontext.h>", kPublic }, + // Exports guaranteed by the C standard + { "<stdint.h>", kPublic, "<inttypes.h>", kPublic }, }; const IncludeMapEntry stdlib_c_include_map[] = { @@ -1315,16 +1409,17 @@ void IncludePicker::ExpandRegexes() { for (const string& regex_key : filepath_include_map_regex_keys) { const vector<MappedInclude>& map_to = filepath_include_map_[regex_key]; // Enclose the regex in ^(...)$ for full match. - llvm::Regex regex(std::string("^(" + regex_key.substr(1) + ")$")); - if (regex.match(hdr, nullptr) && !ContainsQuotedInclude(map_to, hdr)) { + std::regex regex(std::string("^(" + regex_key.substr(1) + ")$")); + if (std::regex_match(hdr, regex) && + !ContainsQuotedInclude(map_to, hdr)) { Extend(&filepath_include_map_[hdr], filepath_include_map_[regex_key]); MarkVisibility(&include_visibility_map_, hdr, include_visibility_map_[regex_key]); } } for (const string& regex_key : friend_to_headers_map_regex_keys) { - llvm::Regex regex(std::string("^(" + regex_key.substr(1) + ")$")); - if (regex.match(hdr, nullptr)) { + std::regex regex(std::string("^(" + regex_key.substr(1) + ")$")); + if (std::regex_match(hdr, regex)) { InsertAllInto(friend_to_headers_map_[regex_key], &friend_to_headers_map_[hdr]); } diff --git a/iwyu_output.cc b/iwyu_output.cc index e6bb851..d805242 100644 --- a/iwyu_output.cc +++ b/iwyu_output.cc @@ -1161,7 +1161,7 @@ void ProcessFullUse(OneUse* use, // We normally ignore uses for builtins, but when there is a mapping defined // for the symbol, we should respect that. So, we need to determine whether // the symbol has any mappings. - bool is_builtin_function = IsBuiltinFunction(use->decl(), use->symbol_name()); + bool is_builtin_function = IsBuiltinFunction(use->decl()); bool is_builtin_function_with_mappings = is_builtin_function && HasMapping(use->symbol_name()); @@ -1987,7 +1987,7 @@ size_t PrintableDiffs(const string& filename, break; } } - if (no_adds_or_deletes) { + if (no_adds_or_deletes && !GlobalFlags().update_comments) { output = "\n(" + filename + " has correct #includes/fwd-decls)\n"; return 0; } diff --git a/iwyu_port.h b/iwyu_port.h index d890575..e1fabb3 100644 --- a/iwyu_port.h +++ b/iwyu_port.h @@ -26,7 +26,7 @@ class FatalMessageEmitter { FatalMessageEmitter(const char* file, int line, const char* message) { stream() << file << ":" << line << ": Assertion failed: " << message; } - LLVM_ATTRIBUTE_NORETURN ~FatalMessageEmitter() { + [[noreturn]] ~FatalMessageEmitter() { stream() << "\n"; ::abort(); #ifdef LLVM_BUILTIN_UNREACHABLE 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 eaf0abc..fcb210e 100755 --- a/iwyu_tool.py +++ b/iwyu_tool.py @@ -42,6 +42,7 @@ import subprocess CORRECT_RE = re.compile(r'^\((.*?) has correct #includes/fwd-decls\)$') SHOULD_ADD_RE = re.compile(r'^(.*?) should add these lines:$') +ADD_RE = re.compile('^(.*?) +// (.*)$') SHOULD_REMOVE_RE = re.compile(r'^(.*?) should remove these lines:$') FULL_LIST_RE = re.compile(r'The full include-list for (.*?):$') END_RE = re.compile(r'^---$') @@ -80,14 +81,17 @@ def clang_formatter(output): elif state[0] == GENERAL: formatted.append(line) elif state[0] == ADD: - formatted.append('%s:1:1: error: add the following line' % state[1]) - formatted.append(line) + match = ADD_RE.match(line) + if match: + formatted.append("%s:1:1: error: add '%s' (%s)" % + (state[1], match.group(1), match.group(2))) + else: + formatted.append("%s:1:1: error: add '%s'" % (state[1], line)) elif state[0] == REMOVE: match = LINES_RE.match(line) line_no = match.group(2) if match else '1' - formatted.append('%s:%s:1: error: remove the following line' % - (state[1], line_no)) - formatted.append(match.group(1)) + formatted.append("%s:%s:1: error: superfluous '%s'" % + (state[1], line_no, match.group(1))) return os.linesep.join(formatted) @@ -363,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 = [] @@ -374,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 12a451f..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.16" +#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/c/libbuiltins-direct.h b/tests/c/libbuiltins-direct.h new file mode 100644 index 0000000..eef6b58 --- /dev/null +++ b/tests/c/libbuiltins-direct.h @@ -0,0 +1,10 @@ +//===--- libbuiltins-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. +// +//===----------------------------------------------------------------------===// + +#include <math.h> diff --git a/tests/c/libbuiltins.c b/tests/c/libbuiltins.c new file mode 100644 index 0000000..4ba3033 --- /dev/null +++ b/tests/c/libbuiltins.c @@ -0,0 +1,34 @@ +//===--- libbuiltins.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. +// +//===----------------------------------------------------------------------===// + +// Library builtins are, like normal builtins, compiled down to an intrinsic, +// but a header still needs to be included for the program to be valid. The math +// library (pow, round, etc) is a typical example. + +// IWYU_ARGS: -I . + +#include "tests/c/libbuiltins-direct.h" + +float kapow(float x) { + // IWYU: pow is...*math.h + return pow(x, 2.0F); +} + +/**** IWYU_SUMMARY + +tests/c/libbuiltins.c should add these lines: +#include <math.h> + +tests/c/libbuiltins.c should remove these lines: +- #include "tests/c/libbuiltins-direct.h" // lines XX-XX + +The full include-list for tests/c/libbuiltins.c: +#include <math.h> // for pow + +***** IWYU_SUMMARY */ diff --git a/tests/cxx/badinc.cc b/tests/cxx/badinc.cc index e58a98f..3c3c3aa 100644 --- a/tests/cxx/badinc.cc +++ b/tests/cxx/badinc.cc @@ -1094,8 +1094,10 @@ int main() { // IWYU: I1_PtrDereferenceClass needs a declaration I1_PtrDereferenceClass* local_i1_ptrdereference_class = 0; int x; - // IWYU: va_list is...*<stdarg.h> - va_list vl; // in gcc, va_list is an internal type, so this tests <built-in> + // va_list is normally in <stdarg.h>, but we already have <stdio.h> + // available, so mappings will source it from there. + // IWYU: va_list is...*<stdio.h> + va_list vl; D1_I1_Typedef d1_i1_typedef; // IWYU: i1_int is...*badinc-i1.h int vararray[i1_int]; @@ -1859,7 +1861,6 @@ int main() { tests/cxx/badinc.cc should add these lines: #include <ctype.h> -#include <stdarg.h> #include <stddef.h> #include <list> #include "tests/cxx/badinc-i1.h" @@ -1889,7 +1890,6 @@ The full include-list for tests/cxx/badinc.cc: #include "tests/cxx/badinc-inl.h" #include <ctype.h> // for isascii #include <setjmp.h> -#include <stdarg.h> // for va_list #include <stddef.h> // for offsetof #include <algorithm> // for find #include <fstream> // for fstream 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/cxx/ctad-d1.h b/tests/cxx/ctad-d1.h new file mode 100644 index 0000000..c492fde --- /dev/null +++ b/tests/cxx/ctad-d1.h @@ -0,0 +1,10 @@ +//===--- ctad-d1.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. +// +//===----------------------------------------------------------------------===// + +#include "tests/cxx/ctad-i1.h" diff --git a/tests/cxx/ctad-i1.h b/tests/cxx/ctad-i1.h new file mode 100644 index 0000000..defd3f0 --- /dev/null +++ b/tests/cxx/ctad-i1.h @@ -0,0 +1,20 @@ +//===--- ctad-i1.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. +// +//===----------------------------------------------------------------------===// + +template <class Func> +struct Deduced { + Deduced(Func&& deferred) : deferred(deferred) { + } + + ~Deduced() { + deferred(); + } + + Func deferred; +}; diff --git a/tests/cxx/ctad.cc b/tests/cxx/ctad.cc new file mode 100644 index 0000000..792343b --- /dev/null +++ b/tests/cxx/ctad.cc @@ -0,0 +1,33 @@ +//===--- ctad.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++17 + +// Test that C++17 CTAD (Class Template Argument Deduction) is recognized as +// pointing back to a template, even if it's not explicitly specialized. + +#include "tests/cxx/ctad-d1.h" + +void f() { + // IWYU: Deduced is...*ctad-i1.h + Deduced d([] {}); +} + +/**** IWYU_SUMMARY + +tests/cxx/ctad.cc should add these lines: +#include "tests/cxx/ctad-i1.h" + +tests/cxx/ctad.cc should remove these lines: +- #include "tests/cxx/ctad-d1.h" // lines XX-XX + +The full include-list for tests/cxx/ctad.cc: +#include "tests/cxx/ctad-i1.h" // for Deduced + +***** IWYU_SUMMARY */ diff --git a/tests/cxx/iwyu_stricter_than_cpp-type_alias.h b/tests/cxx/iwyu_stricter_than_cpp-type_alias.h new file mode 100644 index 0000000..3256f55 --- /dev/null +++ b/tests/cxx/iwyu_stricter_than_cpp-type_alias.h @@ -0,0 +1,76 @@ +//===--- iwyu_stricter_than_cpp-type_alias.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. +// +//===----------------------------------------------------------------------===// + +// The two rules the author has to follow to disable iwyu's +// stricter-than-C++ rule and force it to fall back on the c++ +// requirement (forward-declare ok): +// (1) forward-declare the relevant type +// (2) do not directly #include the definition of the relevant type. + +#include "tests/cxx/iwyu_stricter_than_cpp-d1.h" + +// --- Type aliases. + +// Requires the full type because it does not obey rule (1) +// IWYU: IndirectStruct1 is...*iwyu_stricter_than_cpp-i1.h +using DoesNotForwardDeclareAl = IndirectStruct1; + +// This also does not obey rule (1): it's -d1 that does the fwd-declaring. +// IWYU: IndirectStructForwardDeclaredInD1 is...*iwyu_stricter_than_cpp-i1.h +using DoesNotForwardDeclareProperlyAl = IndirectStructForwardDeclaredInD1; + +// Requires the full type because it does not obey rule (2) +struct DirectStruct1; +using IncludesAl = DirectStruct1; + +// Requires the full type because it does not obey rules (1) *or* (2) +using DoesNotForwardDeclareAndIncludesAl = DirectStruct2; + +// Does not require full type because it obeys all the rules. +struct IndirectStruct2; +using DoesEverythingRightAl = IndirectStruct2; + +// --- Now do it all again, with templates! + +// IWYU: TplIndirectStruct1 is...*iwyu_stricter_than_cpp-i1.h +using TplDoesNotForwardDeclareAl = TplIndirectStruct1<int>; + +using TplDoesNotForwardDeclareProperlyAl +// IWYU: TplIndirectStructForwardDeclaredInD1 is...*iwyu_stricter_than_cpp-i1.h + = TplIndirectStructForwardDeclaredInD1<int>; + +template <typename T> struct TplDirectStruct1; +using TplIncludesAl = TplDirectStruct1<int>; + +using TplDoesNotForwardDeclareAndIncludesAl = TplDirectStruct2<int>; + +template <typename T> struct TplIndirectStruct2; +using TplDoesEverythingRightAl = TplIndirectStruct2<int>; + +// Another way to forward-declare a class template. +template <> struct TplIndirectStruct2<float>; +using TplDoesEverythingRightAgainAl = TplIndirectStruct2<float>; + + +/**** IWYU_SUMMARY + +tests/cxx/iwyu_stricter_than_cpp-type_alias.h should add these lines: +#include "tests/cxx/iwyu_stricter_than_cpp-i1.h" + +tests/cxx/iwyu_stricter_than_cpp-type_alias.h should remove these lines: +- struct DirectStruct1; // lines XX-XX +- template <typename T> struct TplDirectStruct1; // lines XX-XX + +The full include-list for tests/cxx/iwyu_stricter_than_cpp-type_alias.h: +#include "tests/cxx/iwyu_stricter_than_cpp-d1.h" // for DirectStruct1, DirectStruct2, TplDirectStruct1, TplDirectStruct2 +#include "tests/cxx/iwyu_stricter_than_cpp-i1.h" // for IndirectStruct1, IndirectStructForwardDeclaredInD1, TplIndirectStruct1, TplIndirectStructForwardDeclaredInD1 +struct IndirectStruct2; // lines XX-XX +template <typename T> struct TplIndirectStruct2; // lines XX-XX + +***** IWYU_SUMMARY */ diff --git a/tests/cxx/iwyu_stricter_than_cpp.cc b/tests/cxx/iwyu_stricter_than_cpp.cc index d6f70d8..2bfa1c1 100644 --- a/tests/cxx/iwyu_stricter_than_cpp.cc +++ b/tests/cxx/iwyu_stricter_than_cpp.cc @@ -10,6 +10,7 @@ // IWYU_ARGS: -Xiwyu --check_also="tests/cxx/*-autocast.h" \ // -Xiwyu --check_also="tests/cxx/*-fnreturn.h" \ // -Xiwyu --check_also="tests/cxx/*-typedefs.h" \ +// -Xiwyu --check_also="tests/cxx/*-type_alias.h" \ // -Xiwyu --check_also="tests/cxx/*-d2.h" \ // -I . @@ -36,6 +37,7 @@ // when these two conditions are met, and not otherwise. #include "tests/cxx/iwyu_stricter_than_cpp-typedefs.h" +#include "tests/cxx/iwyu_stricter_than_cpp-type_alias.h" #include "tests/cxx/iwyu_stricter_than_cpp-autocast.h" #include "tests/cxx/iwyu_stricter_than_cpp-fnreturn.h" // We include this so the second declaration of TwiceDeclaredFunction @@ -81,6 +83,39 @@ void TestTypedefs() { // all) of the template args as well. } +using DoubleTypedefAl = DoesEverythingRightAl; + +void TestTypeAliases() { + DoesNotForwardDeclareAl dnfd(1); + DoesNotForwardDeclareProperlyAl dnfdp(2); + IncludesAl i(3); + DoesNotForwardDeclareAndIncludesAl dnfdai(4); + // IWYU: IndirectStruct2 is...*iwyu_stricter_than_cpp-i2.h + DoesEverythingRightAl dor(5); + // Because DoubleTypedefAl resolves to DoesEverythingRightAl, we need the + // same things DoesEverythingRightAl does. + // IWYU: IndirectStruct2 is...*iwyu_stricter_than_cpp-i2.h + DoubleTypedefAl dt(6); + + // ...and with templates. + TplDoesNotForwardDeclareAl tdnfd(7); + TplDoesNotForwardDeclareProperlyAl tdnfdp(8); + TplIncludesAl ti(9); + TplDoesNotForwardDeclareAndIncludesAl tdnfdai(10); + // IWYU: TplIndirectStruct2 is...*iwyu_stricter_than_cpp-i2.h + TplDoesEverythingRightAl tdor(11); + // IWYU: TplIndirectStruct2 is...*iwyu_stricter_than_cpp-i2.h + TplDoesEverythingRightAgainAl tdora(12); + + // But if we're in a forward-declare context, we don't require the + // underlying type! + DoesEverythingRightAl* dor_ptr = 0; + TplDoesEverythingRightAgainAl* tdora_ptr = 0; + // ...at least until we dereference the pointer + // IWYU: IndirectStruct2 is...*iwyu_stricter_than_cpp-i2.h + (void) dor_ptr->a; +} + void TestAutocast() { // We need full type of is2 because the declarer of Fn didn't // IWYU: IndirectStruct2 is...*iwyu_stricter_than_cpp-i2.h @@ -159,6 +194,7 @@ The full include-list for tests/cxx/iwyu_stricter_than_cpp.cc: #include "tests/cxx/iwyu_stricter_than_cpp-autocast.h" // for Fn, TplFn #include "tests/cxx/iwyu_stricter_than_cpp-fnreturn.h" // for DoesEverythingRightFn, DoesNotForwardDeclareAndIncludesFn, DoesNotForwardDeclareFn, DoesNotForwardDeclareProperlyFn, IncludesFn, TplDoesEverythingRightAgainFn, TplDoesEverythingRightFn, TplDoesNotForwardDeclareAndIncludesFn, TplDoesNotForwardDeclareFn, TplDoesNotForwardDeclareProperlyFn, TplIncludesFn #include "tests/cxx/iwyu_stricter_than_cpp-i2.h" // for IndirectStruct2, TplIndirectStruct2 +#include "tests/cxx/iwyu_stricter_than_cpp-type_alias.h" // for DoesEverythingRightAl, DoesNotForwardDeclareAl, DoesNotForwardDeclareAndIncludesAl, DoesNotForwardDeclareProperlyAl, IncludesAl, TplDoesEverythingRightAgainAl, TplDoesEverythingRightAl, TplDoesNotForwardDeclareAl, TplDoesNotForwardDeclareAndIncludesAl, TplDoesNotForwardDeclareProperlyAl, TplIncludesAl #include "tests/cxx/iwyu_stricter_than_cpp-typedefs.h" // for DoesEverythingRight, DoesNotForwardDeclare, DoesNotForwardDeclareAndIncludes, DoesNotForwardDeclareProperly, Includes, TplDoesEverythingRight, TplDoesEverythingRightAgain, TplDoesNotForwardDeclare, TplDoesNotForwardDeclareAndIncludes, TplDoesNotForwardDeclareProperly, TplIncludes struct DirectStruct1; struct DirectStruct2; diff --git a/tests/cxx/libbuiltins-direct.h b/tests/cxx/libbuiltins-direct.h new file mode 100644 index 0000000..47afdf6 --- /dev/null +++ b/tests/cxx/libbuiltins-direct.h @@ -0,0 +1,10 @@ +//===--- libbuiltins-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. +// +//===----------------------------------------------------------------------===// + +#include <cmath> diff --git a/tests/cxx/libbuiltins.cc b/tests/cxx/libbuiltins.cc new file mode 100644 index 0000000..d2cdd3e --- /dev/null +++ b/tests/cxx/libbuiltins.cc @@ -0,0 +1,34 @@ +//===--- libbuiltins.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. +// +//===----------------------------------------------------------------------===// + +// Library builtins are, like normal builtins, compiled down to an intrinsic, +// but a header still needs to be included for the program to be valid. The math +// library (std::pow, std::round, etc) is a typical example. + +// IWYU_ARGS: -I . + +#include "tests/cxx/libbuiltins-direct.h" + +float kapow(float x) { + // IWYU: std::pow is...*cmath + return std::pow(x, 2.0F); +} + +/**** IWYU_SUMMARY + +tests/cxx/libbuiltins.cc should add these lines: +#include <cmath> + +tests/cxx/libbuiltins.cc should remove these lines: +- #include "tests/cxx/libbuiltins-direct.h" // lines XX-XX + +The full include-list for tests/cxx/libbuiltins.cc: +#include <cmath> // for pow + +***** IWYU_SUMMARY */ diff --git a/tests/cxx/macro_location_tpl-d1.h b/tests/cxx/macro_location_tpl-d1.h new file mode 100644 index 0000000..cf7712f --- /dev/null +++ b/tests/cxx/macro_location_tpl-d1.h @@ -0,0 +1,10 @@ +//===--- macro_location_tpl-d1.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. +// +//===----------------------------------------------------------------------===// + +#include "tests/cxx/macro_location_tpl-i1.h" diff --git a/tests/cxx/macro_location_tpl-d2.h b/tests/cxx/macro_location_tpl-d2.h new file mode 100644 index 0000000..33da0bb --- /dev/null +++ b/tests/cxx/macro_location_tpl-d2.h @@ -0,0 +1,33 @@ +//===--- macro_location_tpl-d2.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. +// +//===----------------------------------------------------------------------===// + +// Declare a couple of function templates whose specializations are used in a +// macro, to check that author intent hints attribute uses to the right place. +template <typename> +void expansion_template() = delete; +template <> +void expansion_template<int>(); + +template <typename> +void spelling_template() = delete; +template <> +void spelling_template<int>(); + +// As above, but for class templates. +template <typename> +struct ExpansionTemplate { + void method() { + } +}; + +template <typename> +struct SpellingTemplate { + void method() { + } +}; diff --git a/tests/cxx/macro_location_tpl-i1.h b/tests/cxx/macro_location_tpl-i1.h new file mode 100644 index 0000000..460e2a5 --- /dev/null +++ b/tests/cxx/macro_location_tpl-i1.h @@ -0,0 +1,30 @@ +//===--- macro_location_tpl-i1.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. +// +//===----------------------------------------------------------------------===// + +// Forward-declare the primary templates as a signal that specialization uses +// belong in the expansion location. +template <typename> +void expansion_template(); + +template <typename> +class ExpansionTemplate; + +// expansion_template should be attributed to expansion loc. +#define FUNC_TEMPLATE_SPEC_EXPANSION expansion_template<int>() + +// spelling_template should be attributed to this file, because there's no +// forward-declare hint. +#define FUNC_TEMPLATE_SPEC_SPELLING spelling_template<int>(); + +// ExpansionTemplate should be attributed to expansion loc. +#define CLASS_TEMPLATE_SPEC_EXPANSION ExpansionTemplate<int>().method(); + +// SpellingTemplate should be attributed to this file, because there's no +// forward-declare hint. +#define CLASS_TEMPLATE_SPEC_SPELLING SpellingTemplate<int>().method(); diff --git a/tests/cxx/macro_location_tpl.cc b/tests/cxx/macro_location_tpl.cc new file mode 100644 index 0000000..01a0a00 --- /dev/null +++ b/tests/cxx/macro_location_tpl.cc @@ -0,0 +1,46 @@ +//===--- macro_location_tpl.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. +// +//===----------------------------------------------------------------------===// + +// Expands on macro_location test case to also cover template scenarios. Macro +// uses are attributed to spelling location, but forward-declarations of primary +// templates also work as author intent hints for template specializations used +// inside the macro. + +// IWYU_ARGS: -I . + +#include "tests/cxx/macro_location_tpl-d2.h" +#include "tests/cxx/macro_location_tpl-d1.h" + +void use_macro_with_template_spec() { + // IWYU: FUNC_TEMPLATE_SPEC_EXPANSION is defined...*macro_location_tpl-i1.h + FUNC_TEMPLATE_SPEC_EXPANSION; + + // IWYU: FUNC_TEMPLATE_SPEC_SPELLING is defined...*macro_location_tpl-i1.h + FUNC_TEMPLATE_SPEC_SPELLING; + + // IWYU: CLASS_TEMPLATE_SPEC_EXPANSION is defined...*macro_location_tpl-i1.h + CLASS_TEMPLATE_SPEC_EXPANSION; + + // IWYU: CLASS_TEMPLATE_SPEC_SPELLING is defined...*macro_location_tpl-i1.h + CLASS_TEMPLATE_SPEC_SPELLING; +} + +/**** IWYU_SUMMARY + +tests/cxx/macro_location_tpl.cc should add these lines: +#include "tests/cxx/macro_location_tpl-i1.h" + +tests/cxx/macro_location_tpl.cc should remove these lines: +- #include "tests/cxx/macro_location_tpl-d1.h" // lines XX-XX + +The full include-list for tests/cxx/macro_location_tpl.cc: +#include "tests/cxx/macro_location_tpl-d2.h" // for ExpansionTemplate, expansion_template +#include "tests/cxx/macro_location_tpl-i1.h" // for CLASS_TEMPLATE_SPEC_EXPANSION, CLASS_TEMPLATE_SPEC_SPELLING, FUNC_TEMPLATE_SPEC_EXPANSION, FUNC_TEMPLATE_SPEC_SPELLING + +***** IWYU_SUMMARY */ diff --git a/tests/cxx/no_forced_alias_callability-d1.h b/tests/cxx/no_forced_alias_callability-d1.h new file mode 100644 index 0000000..895a73f --- /dev/null +++ b/tests/cxx/no_forced_alias_callability-d1.h @@ -0,0 +1,14 @@ +//===--- no_forced_alias_callability-d1.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. +// +//===----------------------------------------------------------------------===// + +struct ReturnType; + +struct Aliased { + ReturnType doSomething(); +}; diff --git a/tests/cxx/no_forced_alias_callability-d2.h b/tests/cxx/no_forced_alias_callability-d2.h new file mode 100644 index 0000000..7be9a1b --- /dev/null +++ b/tests/cxx/no_forced_alias_callability-d2.h @@ -0,0 +1,18 @@ +//===--- no_forced_alias_callability-d2.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. +// +//===----------------------------------------------------------------------===// + +struct Aliased; + +typedef Aliased Alias; + +/**** IWYU_SUMMARY + +(tests/cxx/no_forced_alias_callability-d2.h has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/cxx/no_forced_alias_callability.cc b/tests/cxx/no_forced_alias_callability.cc new file mode 100644 index 0000000..6d4ce0a --- /dev/null +++ b/tests/cxx/no_forced_alias_callability.cc @@ -0,0 +1,29 @@ +//===--- no_forced_alias_callability.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: -Xiwyu --check_also="tests/cxx/no_forced_alias_callability-d2.h" \ +// -I . + +// Tests that IWYU doesn't require inclusion of an aliased class header +// (...-d1.h) into a header with the alias to provide callability of methods +// of the aliased class if the aliased class is explicitly made forward declared +// in accordance with the IWYU policy + +#include "tests/cxx/no_forced_alias_callability-d1.h" +#include "tests/cxx/no_forced_alias_callability-d2.h" + +int main() { + Alias a; +} + +/**** IWYU_SUMMARY + +(tests/cxx/no_forced_alias_callability.cc has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/cxx/scope_crash.cc b/tests/cxx/scope_crash.cc new file mode 100644 index 0000000..1659e08 --- /dev/null +++ b/tests/cxx/scope_crash.cc @@ -0,0 +1,45 @@ +//===--- scope_crash.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. +// +//===----------------------------------------------------------------------===// + +// This is a weak testcase, but it's the smallest example we've found to +// reproduce an IWYU crash where Sema::TUScope was unexpectedly null. +// +// For some reason libstdc++9's std::map with a value with explicit default +// constructor triggers some path in Sema's constructor lookup that needs a +// non-null TUScope. +// +// Clang or libstdc++ might change so that this can no longer trigger the +// original bug, or so that the bug manifests some other way. But testers can't +// be choosers. + +#include <string> +#include <map> + +struct A {}; + +bool operator<(const A& lhs, const A& rhs) { + return false; +} + +struct B { + // Used to crash with libstdc++ 9, worked without 'explicit' + explicit B() = default; + std::string data; +}; + +void foo(const A& a) { + std::map<A, B> m; + m.erase(a); +} + +/**** IWYU_SUMMARY + +(tests/cxx/scope_crash.cc has correct #includes/fwd-decls) + +***** IWYU_SUMMARY */ diff --git a/tests/cxx/update_comments.cc b/tests/cxx/update_comments.cc new file mode 100644 index 0000000..bf0eccb --- /dev/null +++ b/tests/cxx/update_comments.cc @@ -0,0 +1,28 @@ +//===--- update_comments.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: -Xiwyu --update_comments -I . + +// Test that passing the --update_comments switch to IWYU makes it always print +// the full include-list, with up to date "// for XYZ" comments. + +#include "tests/cxx/indirect.h" // for SomethingElse + +IndirectClass indirect; + +/**** IWYU_SUMMARY + +tests/cxx/update_comments.cc should add these lines: + +tests/cxx/update_comments.cc should remove these lines: + +The full include-list for tests/cxx/update_comments.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_ |