summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSylvestre Ledru <sylvestre@debian.org>2020-11-26 15:37:46 +0100
committerSylvestre Ledru <sylvestre@debian.org>2020-11-26 15:37:46 +0100
commitfba8fe3a1de68d9e7e23347561e248cebfb051ce (patch)
treec2cf5e2e08626627572d788e3926b114c0fd3ba0
parentb97db87f8b21543941cf220f49d89eea49c99295 (diff)
New upstream version 8.15
-rw-r--r--.gitignore2
-rw-r--r--.travis.yml28
-rw-r--r--CMakeLists.txt25
-rw-r--r--README.md3
-rw-r--r--boost-1.64-all-private.imp31
-rwxr-xr-xfix_includes.py30
-rwxr-xr-xfix_includes_test.py28
-rw-r--r--gcc.libc.imp1
-rw-r--r--gcc.symbols.imp4
-rwxr-xr-xgenerate_qt_mappings.py140
-rw-r--r--iwyu.cc375
-rw-r--r--iwyu_ast_util.cc126
-rw-r--r--iwyu_ast_util.h68
-rw-r--r--iwyu_driver.cc2
-rw-r--r--iwyu_globals.cc4
-rw-r--r--iwyu_globals.h2
-rw-r--r--iwyu_include_picker.cc7
-rw-r--r--iwyu_include_picker.h4
-rw-r--r--iwyu_lexer_utils.cc2
-rw-r--r--iwyu_location_util.h2
-rw-r--r--iwyu_output.cc26
-rw-r--r--iwyu_output.h2
-rw-r--r--iwyu_path_util.cc6
-rw-r--r--iwyu_port.h2
-rw-r--r--iwyu_preprocessor.cc4
-rwxr-xr-xiwyu_tool.py9
-rwxr-xr-xiwyu_tool_test.py13
-rw-r--r--iwyu_version.h2
-rw-r--r--more_tests/iwyu_include_picker_test.cc13
-rwxr-xr-xrun_iwyu_tests.py5
-rw-r--r--tests/cxx/alias_template.cc25
-rw-r--r--tests/cxx/alias_template_use-d1.h10
-rw-r--r--tests/cxx/alias_template_use-i1.h14
-rw-r--r--tests/cxx/alias_template_use-i2.h11
-rw-r--r--tests/cxx/alias_template_use.cc35
-rw-r--r--tests/cxx/badinc.cc54
-rw-r--r--tests/cxx/builtins_new_included.cc31
-rw-r--r--tests/cxx/operator_new.cc86
-rw-r--r--tests/cxx/placement_new-d1.h15
-rw-r--r--tests/cxx/placement_new-i1.h24
-rw-r--r--tests/cxx/placement_new.cc151
-rw-r--r--tests/cxx/precomputed_tpl_args.cc5
-rw-r--r--tests/cxx/sizeof_in_template_arg.cc41
-rw-r--r--tests/cxx/specialization_needs_decl-d1.h2
-rw-r--r--tests/cxx/specialization_needs_decl-i1.h18
-rw-r--r--tests/cxx/specialization_needs_decl.cc11
-rw-r--r--tests/cxx/template_specialization.cc13
-rw-r--r--tests/cxx/typedef_in_template.cc68
48 files changed, 1116 insertions, 464 deletions
diff --git a/.gitignore b/.gitignore
index 2df9878..297e5e2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,5 +6,7 @@ __pycache__
.gdb_history
compile_commands.json
+tags
+TAGS
/build
diff --git a/.travis.yml b/.travis.yml
index 8caec05..9f6270d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,44 +1,32 @@
-dist: xenial
+dist: bionic
language: cpp
addons:
apt:
sources:
- - sourceline: 'deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main'
+ - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main'
key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'
packages:
+ - cmake
- ninja-build
- # TODO: These should really be the snapshots packages, but they are currently
- # broken. Remove the version suffix once they get fixed (see issue #642 for
- # more info)
- - llvm-10-dev
- - llvm-10-tools
- - libclang-10-dev
- - clang-10
+ - llvm-11-dev
+ - libclang-11-dev
+ - clang-11
before_install:
- # Install a supported cmake version (>= 3.4.3)
- - wget -O cmake.sh https://cmake.org/files/v3.10/cmake-3.10.0-Linux-x86_64.sh
- - sudo sh cmake.sh --skip-license --exclude-subdir --prefix=/usr/local
-
# Extract the version number from the most-recently installed LLVM
- VERSION=`ls -t /usr/lib/ | grep '^llvm-' | head -n 1 | sed -E 's/llvm-(.+)/\1/'`
# Absolute paths to LLVM's root and bin directory
- - ROOT_PATH=`llvm-config-$VERSION --prefix`
+ - PREFIX_PATH=`llvm-config-$VERSION --prefix`
- BIN_PATH=`llvm-config-$VERSION --bindir`
- # TODO: Remove these hacky fixups
- - (cd $ROOT_PATH/lib && sudo ln -rsf libclang-cpp.so.1 libclang-cpp-$VERSION.so.1)
- - sudo touch $ROOT_PATH/lib/libExampleIRTransforms.a
- - sudo touch $ROOT_PATH/lib/libBye.a
-
script:
# Build IWYU
- mkdir build
- cd build
- - cmake -GNinja -DCMAKE_PREFIX_PATH=$ROOT_PATH -DCMAKE_C_COMPILER=$BIN_PATH/clang -DCMAKE_CXX_COMPILER=$BIN_PATH/clang++ -DCMAKE_INSTALL_PREFIX=./ ../
+ - cmake -GNinja -DCMAKE_PREFIX_PATH=$PREFIX_PATH -DCMAKE_C_COMPILER=$BIN_PATH/clang -DCMAKE_CXX_COMPILER=$BIN_PATH/clang++ -DCMAKE_INSTALL_PREFIX=./ ../
- ninja
# Test IWYU
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8fe0411..60477ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,6 +26,10 @@ endif()
message(STATUS "IWYU: configuring for LLVM ${LLVM_VERSION}...")
+# The good default is given by the llvm toolchain installation itself, but still in
+# case both static and shared libraries are available, allow to override that default.
+option(IWYU_LINK_CLANG_DYLIB "Link against the clang dynamic library" ${CLANG_LINK_CLANG_DYLIB})
+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_definitions(${LLVM_DEFINITIONS})
@@ -64,21 +68,22 @@ if (NOT IWYU_IN_TREE)
# Use only major.minor.patch for the resource directory structure; some
# platforms include suffix in LLVM_VERSION.
set(llvm_ver ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH})
- set(clang_headers_src ${CMAKE_PREFIX_PATH}/lib/clang/${llvm_ver}/include)
- set(clang_headers_dst ${CMAKE_BINARY_DIR}/lib/clang/${llvm_ver}/include)
+ set(clang_headers_src "${LLVM_LIBRARY_DIR}/clang/${llvm_ver}/include")
+ set(clang_headers_dst "${CMAKE_BINARY_DIR}/lib/clang/${llvm_ver}/include")
- file(GLOB_RECURSE in_files RELATIVE ${clang_headers_src} ${clang_headers_src}/*)
+ file(GLOB_RECURSE in_files RELATIVE "${clang_headers_src}"
+ "${clang_headers_src}/*")
set(out_files)
foreach (file ${in_files})
- set(src ${clang_headers_src}/${file})
- set(dst ${clang_headers_dst}/${file})
+ set(src "${clang_headers_src}/${file}")
+ set(dst "${clang_headers_dst}/${file}")
- add_custom_command(OUTPUT ${dst}
- DEPENDS ${src}
- COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst}
+ add_custom_command(OUTPUT "${dst}"
+ DEPENDS "${src}"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different "${src}" "${dst}"
COMMENT "Copying clang's ${file}...")
- list(APPEND out_files ${dst})
+ list(APPEND out_files "${dst}")
endforeach()
add_custom_target(clang-resource-headers ALL DEPENDS ${out_files})
@@ -105,7 +110,7 @@ if (MSVC)
endif()
# If only clang-cpp is available, we take that.
-if (TARGET clang-cpp AND NOT TARGET clangBasic)
+if (IWYU_LINK_CLANG_DYLIB)
target_link_libraries(include-what-you-use PRIVATE clang-cpp)
else()
target_link_libraries(include-what-you-use
diff --git a/README.md b/README.md
index 78ede38..24ff71c 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,10 @@ We assume you already have compiled LLVM and Clang libraries on your system, eit
| 6 | 0.10 | `clang_6.0` |
| 7 | 0.11 | `clang_7.0` |
| 8 | 0.12 | `clang_8.0` |
+| 9 | 0.13 | `clang_9.0` |
+| 10 | 0.14 | `clang_10` |
| ... | ... | ... |
+| trunk | master | `master` |
> NOTE: If you use the Debian/Ubuntu packaging available from <https://apt.llvm.org>, you'll need the following packages installed:
>
diff --git a/boost-1.64-all-private.imp b/boost-1.64-all-private.imp
index 5f32299..439e4d2 100644
--- a/boost-1.64-all-private.imp
+++ b/boost-1.64-all-private.imp
@@ -1,15 +1,13 @@
[
-#grep -r '^ *# *include' boost/ | grep -e "boost/[^:]*/detail/.*hp*:" -e "boost/[^:]*/impl/.*hp*:" | grep -e "\:.*/detail/" -e "\:.*/impl/" | perl -nle 'm/^([^:]+).*["<]([^>]+)[">]/ && print qq@ { include: ["<$2>", private, "<$1>", private ] },@' | grep -e \\[\"\<boost/ | sort -u
-#remove circular dependencies
-# boost/fusion/container/set/detail/value_of_data_impl.hpp with itself...
-#
-# { include: ["<boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp>", private, "<boost/numeric/odeint/integrate/detail/integrate_n_steps.hpp>", private ] },
-# { include: ["<boost/numeric/odeint/integrate/detail/integrate_n_steps.hpp>", private, "<boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp>", private ] },
-#
-# { include: ["<boost/python/detail/type_list.hpp>", private, "<boost/python/detail/type_list_impl.hpp>", private ] },
-# { include: ["<boost/python/detail/type_list.hpp>", private, "<boost/python/detail/type_list_impl_no_pts.hpp>", private ] },
-# { include: ["<boost/python/detail/type_list_impl.hpp>", private, "<boost/python/detail/type_list.hpp>", private ] },
-# { include: ["<boost/python/detail/type_list_impl_no_pts.hpp>", private, "<boost/python/detail/type_list.hpp>", private ] },
+# grep -r '^ *# *include' boost/ | grep -e "boost/[^:]*/detail/.*hp*:" -e "boost/[^:]*/impl/.*hp*:" | grep -e "\:.*/detail/" -e "\:.*/impl/" | perl -nle 'm/^([^:]+).*["<]([^>]+)[">]/ && print qq@ { include: ["<$2>", private, "<$1>", private ] },@' | grep -e \\[\"\<boost/ | sort -u
+# manually remove circular dependencies
+# * boost/fusion/container/set/detail/value_of_data_impl.hpp with itself
+# * boost/variant/detail/multivisitors_cpp14_based.hpp with itself
+# * boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp with boost/numeric/odeint/integrate/detail/integrate_n_steps.hpp and back
+# * boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp with boost/numeric/odeint/integrate/detail/integrate_const.hpp and back
+# * boost/numeric/odeint/iterator/integrate/detail/integrate_adaptive.hpp with boost/numeric/odeint/iterator/integrate/detail/integrate_const.hpp and back
+# * boost/python/detail/type_list.hpp with boost/python/detail/type_list_impl.hpp and back
+# * boost/python/detail/type_list.hpp with boost/python/detail/type_list_impl_no_pts.hpp and back
{ include: ["<boost/accumulators/numeric/detail/function_n.hpp>", private, "<boost/accumulators/numeric/detail/function2.hpp>", private ] },
{ include: ["<boost/accumulators/numeric/detail/function_n.hpp>", private, "<boost/accumulators/numeric/detail/function3.hpp>", private ] },
@@ -4360,10 +4358,6 @@
{ include: ["<boost/numeric/interval/detail/msvc_rounding_control.hpp>", private, "<boost/numeric/interval/detail/x86_rounding_control.hpp>", private ] },
{ include: ["<boost/numeric/interval/detail/test_input.hpp>", private, "<boost/numeric/interval/detail/division.hpp>", private ] },
{ include: ["<boost/numeric/interval/detail/x86gcc_rounding_control.hpp>", private, "<boost/numeric/interval/detail/x86_rounding_control.hpp>", private ] },
- { include: ["<boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp>", private, "<boost/numeric/odeint/integrate/detail/integrate_const.hpp>", private ] },
- { include: ["<boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp>", private, "<boost/numeric/odeint/integrate/detail/integrate_n_steps.hpp>", private ] },
- { include: ["<boost/numeric/odeint/integrate/detail/integrate_const.hpp>", private, "<boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp>", private ] },
- { include: ["<boost/numeric/odeint/integrate/detail/integrate_n_steps.hpp>", private, "<boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp>", private ] },
{ include: ["<boost/numeric/odeint/iterator/detail/ode_iterator_base.hpp>", private, "<boost/numeric/odeint/iterator/impl/adaptive_iterator_impl.hpp>", private ] },
{ include: ["<boost/numeric/odeint/iterator/detail/ode_iterator_base.hpp>", private, "<boost/numeric/odeint/iterator/impl/const_step_iterator_impl.hpp>", private ] },
{ include: ["<boost/numeric/odeint/iterator/detail/ode_iterator_base.hpp>", private, "<boost/numeric/odeint/iterator/impl/n_step_iterator_impl.hpp>", private ] },
@@ -4372,9 +4366,7 @@
{ include: ["<boost/numeric/odeint/iterator/integrate/detail/functors.hpp>", private, "<boost/numeric/odeint/iterator/integrate/detail/integrate_const.hpp>", private ] },
{ include: ["<boost/numeric/odeint/iterator/integrate/detail/functors.hpp>", private, "<boost/numeric/odeint/iterator/integrate/detail/integrate_n_steps.hpp>", private ] },
{ include: ["<boost/numeric/odeint/iterator/integrate/detail/functors.hpp>", private, "<boost/numeric/odeint/iterator/integrate/detail/integrate_times.hpp>", private ] },
- { include: ["<boost/numeric/odeint/iterator/integrate/detail/integrate_adaptive.hpp>", private, "<boost/numeric/odeint/iterator/integrate/detail/integrate_const.hpp>", private ] },
{ include: ["<boost/numeric/odeint/iterator/integrate/detail/integrate_adaptive.hpp>", private, "<boost/numeric/odeint/iterator/integrate/detail/integrate_n_steps.hpp>", private ] },
- { include: ["<boost/numeric/odeint/iterator/integrate/detail/integrate_const.hpp>", private, "<boost/numeric/odeint/iterator/integrate/detail/integrate_adaptive.hpp>", private ] },
{ include: ["<boost/numeric/odeint/stepper/detail/generic_rk_call_algebra.hpp>", private, "<boost/numeric/odeint/stepper/detail/generic_rk_algorithm.hpp>", private ] },
{ include: ["<boost/numeric/odeint/stepper/detail/generic_rk_operations.hpp>", private, "<boost/numeric/odeint/stepper/detail/generic_rk_algorithm.hpp>", private ] },
{ include: ["<boost/numeric/odeint/util/detail/less_with_sign.hpp>", private, "<boost/numeric/odeint/integrate/detail/integrate_adaptive.hpp>", private ] },
@@ -4914,10 +4906,6 @@
{ include: ["<boost/python/detail/scope.hpp>", private, "<boost/python/detail/defaults_def.hpp>", private ] },
{ include: ["<boost/python/detail/sfinae.hpp>", private, "<boost/python/detail/enable_if.hpp>", private ] },
{ include: ["<boost/python/detail/signature.hpp>", private, "<boost/python/detail/caller.hpp>", private ] },
- { include: ["<boost/python/detail/type_list.hpp>", private, "<boost/python/detail/type_list_impl.hpp>", private ] },
- { include: ["<boost/python/detail/type_list.hpp>", private, "<boost/python/detail/type_list_impl_no_pts.hpp>", private ] },
- { include: ["<boost/python/detail/type_list_impl.hpp>", private, "<boost/python/detail/type_list.hpp>", private ] },
- { include: ["<boost/python/detail/type_list_impl_no_pts.hpp>", private, "<boost/python/detail/type_list.hpp>", private ] },
{ include: ["<boost/python/detail/value_is_xxx.hpp>", private, "<boost/python/detail/value_is_shared_ptr.hpp>", private ] },
{ include: ["<boost/python/detail/wrap_python.hpp>", private, "<boost/python/detail/prefix.hpp>", private ] },
{ include: ["<boost/qvm/detail/determinant_impl.hpp>", private, "<boost/qvm/detail/cofactor_impl.hpp>", private ] },
@@ -5287,7 +5275,6 @@
{ include: ["<boost/variant/detail/has_result_type.hpp>", private, "<boost/variant/detail/apply_visitor_delayed.hpp>", private ] },
{ include: ["<boost/variant/detail/has_result_type.hpp>", private, "<boost/variant/detail/apply_visitor_unary.hpp>", private ] },
{ include: ["<boost/variant/detail/move.hpp>", private, "<boost/variant/detail/initializer.hpp>", private ] },
- { include: ["<boost/variant/detail/multivisitors_cpp14_based.hpp>", private, "<boost/variant/detail/multivisitors_cpp14_based.hpp>", private ] },
{ include: ["<boost/variant/detail/substitute_fwd.hpp>", private, "<boost/variant/detail/substitute.hpp>", private ] },
{ include: ["<boost/variant/detail/substitute.hpp>", private, "<boost/variant/detail/enable_recursive.hpp>", private ] },
{ include: ["<boost/vmd/detail/adjust_tuple_type.hpp>", private, "<boost/vmd/detail/recurse/equal/equal_headers.hpp>", private ] },
diff --git a/fix_includes.py b/fix_includes.py
index f1cfbf2..7600361 100755
--- a/fix_includes.py
+++ b/fix_includes.py
@@ -135,7 +135,7 @@ _IWYU_PRAGMA_ASSOCIATED_RE = re.compile(r'IWYU\s*pragma:\s*associated')
# we fold _C_COMMENT_START_RE and _C_COMMENT_END_RE into _COMMENT_LINE_RE.
# The _NAMESPACE_CONTINUE_ALLMAN_MIXED_RE is also set on lines when Allman
# and mixed namespaces are detected but the RE is too easy to match to add
-# under normal circumstances (must always be proceded by Allman/mixed).
+# under normal circumstances (must always be preceded by Allman/mixed).
_LINE_TYPES = [_COMMENT_LINE_RE, _BLANK_LINE_RE,
_NAMESPACE_START_RE, _NAMESPACE_START_ALLMAN_RE,
_NAMESPACE_START_MIXED_RE, _NAMESPACE_END_RE,
@@ -235,6 +235,10 @@ class IWYUOutputRecord(object):
# report, though, forward-declares inside '#if 0' or similar.)
self.seen_forward_declare_lines = set()
+ # Those spans which pertain to nested forward declarations (i.e. of nested
+ # classes). This set should be a subset of self.seen_forward_declare_lines.
+ self.nested_forward_declare_lines = set()
+
# A set of each line in the iwyu 'add' section.
self.includes_and_forward_declares_to_add = OrderedSet()
@@ -261,6 +265,7 @@ class IWYUOutputRecord(object):
self.lines_to_delete.intersection_update(other.lines_to_delete)
self.some_include_lines.update(other.some_include_lines)
self.seen_forward_declare_lines.update(other.seen_forward_declare_lines)
+ self.nested_forward_declare_lines.update(other.nested_forward_declare_lines)
self.includes_and_forward_declares_to_add.update(
other.includes_and_forward_declares_to_add)
self.full_include_lines.update(other.full_include_lines)
@@ -443,8 +448,10 @@ class IWYUOutputParser(object):
continue
m = self._LINE_NUMBERS_COMMENT_RE.search(line)
if m:
- retval.seen_forward_declare_lines.add((int(m.group(1)),
- int(m.group(2))+1))
+ line_range = (int(m.group(1)), int(m.group(2))+1)
+ retval.seen_forward_declare_lines.add(line_range)
+ if '::' in line:
+ retval.nested_forward_declare_lines.add(line_range)
# IWYUOutputRecord.includes_and_forward_declares_to_add
for line in self.lines_by_section.get(self._ADD_SECTION_RE, []):
@@ -503,6 +510,10 @@ class LineInfo(object):
# of the class/struct. For other types of lines, this is None.
self.key = None
+ # If this is a forward-declaration of a nested class, then this will be
+ # True.
+ self.is_nested_forward_declaration = False
+
def __str__(self):
if self.deleted:
line = 'XX-%s-XX' % self.line
@@ -771,6 +782,13 @@ def _CalculateLineTypesAndKeys(file_lines, iwyu_record):
% (iwyu_record.filename, line_number))
file_lines[line_number].type = _FORWARD_DECLARE_RE
+ for (start_line, end_line) in iwyu_record.nested_forward_declare_lines:
+ for line_number in range(start_line, end_line):
+ if line_number >= len(file_lines):
+ raise FixIncludesError('iwyu line number %s:%d is past file-end'
+ % (iwyu_record.filename, line_number))
+ file_lines[line_number].is_nested_forward_declaration = True
+
# While we're at it, let's do a bit more sanity checking on iwyu_record.
for line_number in iwyu_record.lines_to_delete:
if line_number >= len(file_lines):
@@ -1281,7 +1299,8 @@ def _ShouldInsertBlankLine(decorated_move_span, next_decorated_move_span,
def _GetToplevelReorderSpans(file_lines):
- """Returns a sorted list of all reorder_spans not inside an #ifdef/namespace.
+ """Returns a sorted list of all reorder_spans not inside an
+ #ifdef/namespace/class.
This routine looks at all the reorder_spans in file_lines, ignores
reorder spans inside #ifdefs and namespaces -- except for the 'header
@@ -1337,7 +1356,8 @@ def _GetToplevelReorderSpans(file_lines):
good_reorder_spans = []
for reorder_span in reorder_spans:
for line_number in range(*reorder_span):
- if in_ifdef[line_number] or in_namespace[line_number]:
+ if (in_ifdef[line_number] or in_namespace[line_number] or
+ file_lines[line_number].is_nested_forward_declaration):
break
else: # for/else
good_reorder_spans.append(reorder_span) # never in ifdef or namespace
diff --git a/fix_includes_test.py b/fix_includes_test.py
index b01f145..380da54 100755
--- a/fix_includes_test.py
+++ b/fix_includes_test.py
@@ -2945,6 +2945,34 @@ template <typename T> class B; // lines 4-5
self.RegisterFileContents({'dont_remove_template_lines': infile})
self.ProcessAndTest(iwyu_output)
+ def testDontRemoveSimilarNestedDeclarations(self):
+ """Tests we don't accidentally think repeated nested forward declarations
+ are dupes."""
+ infile = """\
+#include <notused.h> ///-
+
+class A {
+ class Inner;
+};
+
+class B {
+ class Inner;
+};
+"""
+ iwyu_output = """\
+dont_remove_similar_nested should add these lines:
+
+dont_remove_similar_nested should remove these lines:
+- #include <notused.h> // lines 1-1
+
+The full include-list for dont_remove_similar_nested:
+class A::Inner; // lines 4-4
+class B::Inner; // lines 8-8
+---
+"""
+ self.RegisterFileContents({'dont_remove_similar_nested': infile})
+ self.ProcessAndTest(iwyu_output)
+
def testNestedNamespaces(self):
infile = """\
// Copyright 2010
diff --git a/gcc.libc.imp b/gcc.libc.imp
index 66e9fdb..1d436f5 100644
--- a/gcc.libc.imp
+++ b/gcc.libc.imp
@@ -97,6 +97,7 @@
{ include: [ "<bits/syslog-path.h>", private, "<sys/syslog.h>", private ] },
{ include: [ "<bits/syslog.h>", private, "<sys/syslog.h>", private ] },
{ include: [ "<bits/termios.h>", private, "<termios.h>", public ] },
+ { include: [ "<bits/time.h>", private, "<time.h>", public ] },
{ include: [ "<bits/time.h>", private, "<sys/time.h>", public ] },
{ include: [ "<bits/timerfd.h>", private, "<sys/timerfd.h>", public ] },
{ include: [ "<bits/timex.h>", private, "<sys/timex.h>", public ] },
diff --git a/gcc.symbols.imp b/gcc.symbols.imp
index 9f498d8..614cb56 100644
--- a/gcc.symbols.imp
+++ b/gcc.symbols.imp
@@ -57,6 +57,7 @@
{ symbol: [ "intptr_t", private, "<unistd.h>", public ] },
{ symbol: [ "key_t", private, "<sys/types.h>", public ] },
{ symbol: [ "key_t", private, "<sys/ipc.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, "<sys/ipc.h>", public ] },
@@ -77,6 +78,7 @@
{ symbol: [ "pid_t", private, "<termios.h>", public ] },
{ symbol: [ "pid_t", private, "<time.h>", public ] },
{ symbol: [ "pid_t", private, "<utmpx.h>", public ] },
+ { symbol: [ "ptrdiff_t", private, "<stddef.h>", public ] },
{ symbol: [ "sigset_t", private, "<signal.h>", public ] },
{ symbol: [ "sigset_t", private, "<sys/epoll.h>", public ] },
{ symbol: [ "sigset_t", private, "<sys/select.h>", public ] },
@@ -105,6 +107,8 @@
{ symbol: [ "uid_t", private, "<sys/stat.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, "<stdio.h>", public ] },
diff --git a/generate_qt_mappings.py b/generate_qt_mappings.py
index 2a8cd51..3c45237 100755
--- a/generate_qt_mappings.py
+++ b/generate_qt_mappings.py
@@ -22,42 +22,144 @@ Example usage :
from __future__ import print_function
import argparse
import glob
+import json
import os
import re
import sys
+OUTFILEHDR = ("# Do not edit! This file was generated by the script %s." %
+ os.path.basename(__file__))
+
+QOBJECT_SYMBOLS = [
+ "QObjectList",
+ "qFindChildren",
+ "qobject_cast",
+ "QT_NO_NARROWING_CONVERSIONS_IN_CONNECT",
+ "Q_CLASSINFO",
+ "Q_DISABLE_COPY",
+ "Q_DISABLE_COPY_MOVE",
+ "Q_DISABLE_MOVE",
+ "Q_EMIT",
+ "Q_ENUM",
+ "Q_ENUM_NS",
+ "Q_FLAG",
+ "Q_FLAG_NS",
+ "Q_GADGET",
+ "Q_INTERFACES",
+ "Q_INVOKABLE",
+ "Q_NAMESPACE",
+ "Q_NAMESPACE_EXPORT",
+ "Q_OBJECT",
+ "Q_PROPERTY",
+ "Q_REVISION",
+ "Q_SET_OBJECT_NAME",
+ "Q_SIGNAL",
+ "Q_SIGNALS",
+ "Q_SLOT",
+ "Q_SLOTS",
+ "emit",
+ "slots",
+ "signals",
+ "SIGNAL",
+ "SLOT",
+]
+
+
+class QtHeader(object):
+ """ Carry data associated with a Qt header """
+ def __init__(self, headername):
+ self.headername = headername
+ self.classname = os.path.basename(headername)
+ self.modulename = os.path.basename(os.path.dirname(headername))
+ self._private_headers = None
+
+ def get_private_headers(self):
+ """ Return a list of headernames included by this header """
+ if self._private_headers is None:
+ with open(self.headername, 'r') as headerfile:
+ included = re.findall(r'#include "(.*)\.h"', headerfile.read())
+ self._private_headers = list(included)
+ return self._private_headers
+
+
+def build_imp_lines(symbols_map, includes_map):
+ """ Generate a big string containing the mappings in .imp format.
+
+ This should ideally return a jsonable structure instead, and use json.dump
+ to write it to the output file directly. But there doesn't seem to be a
+ simple way to convince Python's json library to generate a "packed"
+ formatting, it always prefers to wrap dicts onto multiple lines.
+
+ Cheat, and use json.dumps for escaping and build a string instead.
+ """
+ root = []
+
+ def jsonline(mapping):
+ return " " + json.dumps(mapping)
+
+ for symbol, header in symbols_map:
+ map_to = "<" + header + ">"
+ root.append(jsonline({"symbol": [symbol, "private", map_to, "public"]}))
+
+ for module, include, header in includes_map:
+ # Use regex map-from to match both quoted and angled includes and
+ # optional directory prefix (e.g. <QtCore/qnamespace.h> is equivalent to
+ # "qnamespace.h").
+ map_from = r'@["<](%s/)?%s\.h[">]' % (module, include)
+ map_to = "<" + header + ">"
+ root.append(jsonline({"include": [map_from, "private",
+ map_to, "public"]}))
+
+ lines = "[\n"
+ lines += ",\n".join(root)
+ lines += "\n]\n"
+ return lines
+
+
+def add_mapping_rules(header, symbols_map, includes_map):
+ """ Add symbol and include mappings for a Qt module. """
+ symbols_map += [(header.classname, header.classname)]
+ for include in header.get_private_headers():
+ includes_map += [(header.modulename, include, header.classname)]
+
+
def main(qt_include_dir, output_file):
+ """ Entry point. """
symbols_map = []
includes_map = []
+ deferred_headers = []
- headers = glob.glob(os.path.join(args.qt_include_dir, '**/*[!.h]'))
+ # Add manual overrides.
+ symbols_map += [("qDebug", "QtGlobal")]
+ symbols_map += [(symbol, "QObject") for symbol in QOBJECT_SYMBOLS]
+ includes_map += [("QtCore", "qnamespace", "Qt")]
+
+ # Collect mapping information from Qt directory tree.
+ headers = glob.glob(os.path.join(qt_include_dir, '**/*[!.h]'))
for header in headers:
if os.path.isdir(header):
continue
- class_name = os.path.basename(header)
- module_name = os.path.basename(os.path.dirname(header))
+ header = QtHeader(header)
+ if header.classname == "QInternal":
+ continue
- symbols_map += ['{ symbol: [ "%s", "private", ' % class_name
- + '"<%s>", "public" ] }' % class_name]
+ if header.classname == header.modulename:
+ deferred_headers.append(header)
+ else:
+ add_mapping_rules(header, symbols_map, includes_map)
- with open(header, 'r') as f:
- content = f.read()
+ for header in deferred_headers:
+ add_mapping_rules(header, symbols_map, includes_map)
- includes = re.findall(r'#include "(.*)\.h"', content)
- for include in includes:
- includes_map += [
- '{ include: [ "@[\\"<](%s/)?%s\\\\.h[\\">]", ' % (
- module_name, include)
- + '"private", "<%s>", "public" ] }' % class_name]
+ # Transform to .imp-style format and write to output file.
+ lines = build_imp_lines(symbols_map, includes_map)
+ with open(output_file, 'w') as outfile:
+ print(OUTFILEHDR, file=outfile)
+ print(lines, file=outfile)
- with open(args.output_file, 'w') as f:
- print("# Do not edit! This file was generated by the script %s." %
- os.path.basename(__file__), file=f)
- print("[", file=f)
- print(" %s" % ",\n ".join(symbols_map + includes_map), file=f)
- print("]", file=f)
+ return 0
if __name__ == "__main__":
diff --git a/iwyu.cc b/iwyu.cc
index 37c7e99..931d57b 100644
--- a/iwyu.cc
+++ b/iwyu.cc
@@ -89,7 +89,6 @@
#include <algorithm> // for swap, find, make_pair
#include <cstddef> // for size_t
-#include <cstdio> // for snprintf
#include <cstdlib> // for atoi, exit
#include <cstring>
#include <deque> // for swap
@@ -232,12 +231,6 @@ using std::vector;
namespace {
-string IntToString(int i) {
- char buf[64]; // big enough for any number
- snprintf(buf, sizeof(buf), "%d", i);
- return buf;
-}
-
bool CanIgnoreLocation(SourceLocation loc) {
// If we're in a macro expansion, we always want to treat this as
// being in the expansion location, never the as-written location,
@@ -327,17 +320,17 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
void set_current_ast_node(ASTNode* an) { current_ast_node_ = an; }
bool TraverseDecl(Decl* decl) {
- if (current_ast_node_->StackContainsContent(decl))
+ if (current_ast_node_ && current_ast_node_->StackContainsContent(decl))
return true; // avoid recursion
- ASTNode node(decl, *GlobalSourceManager());
+ ASTNode node(decl);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
return Base::TraverseDecl(decl);
}
bool TraverseStmt(Stmt* stmt) {
- if (current_ast_node_->StackContainsContent(stmt))
+ if (current_ast_node_ && current_ast_node_->StackContainsContent(stmt))
return true; // avoid recursion
- ASTNode node(stmt, *GlobalSourceManager());
+ ASTNode node(stmt);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
return Base::TraverseStmt(stmt);
}
@@ -346,9 +339,9 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
if (qualtype.isNull())
return Base::TraverseType(qualtype);
const Type* type = qualtype.getTypePtr();
- if (current_ast_node_->StackContainsContent(type))
+ if (current_ast_node_ && current_ast_node_->StackContainsContent(type))
return true; // avoid recursion
- ASTNode node(type, *GlobalSourceManager());
+ ASTNode node(type);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
return Base::TraverseType(qualtype);
}
@@ -370,9 +363,9 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
if (typeloc.getAs<QualifiedTypeLoc>()) {
typeloc = typeloc.getUnqualifiedLoc();
}
- if (current_ast_node_->StackContainsContent(&typeloc))
+ if (current_ast_node_ && current_ast_node_->StackContainsContent(&typeloc))
return true; // avoid recursion
- ASTNode node(&typeloc, *GlobalSourceManager());
+ ASTNode node(&typeloc);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
return Base::TraverseTypeLoc(typeloc);
}
@@ -380,7 +373,7 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
bool TraverseNestedNameSpecifier(NestedNameSpecifier* nns) {
if (nns == nullptr)
return true;
- ASTNode node(nns, *GlobalSourceManager());
+ ASTNode node(nns);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
if (!this->getDerived().VisitNestedNameSpecifier(nns))
return false;
@@ -390,7 +383,7 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc nns_loc) {
if (!nns_loc) // using NNSLoc::operator bool()
return true;
- ASTNode node(&nns_loc, *GlobalSourceManager());
+ ASTNode node(&nns_loc);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
// TODO(csilvers): have VisitNestedNameSpecifierLoc instead.
if (!this->getDerived().VisitNestedNameSpecifier(
@@ -400,7 +393,7 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
}
bool TraverseTemplateName(TemplateName template_name) {
- ASTNode node(&template_name, *GlobalSourceManager());
+ ASTNode node(&template_name);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
if (!this->getDerived().VisitTemplateName(template_name))
return false;
@@ -408,7 +401,7 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
}
bool TraverseTemplateArgument(const TemplateArgument& arg) {
- ASTNode node(&arg, *GlobalSourceManager());
+ ASTNode node(&arg);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
if (!this->getDerived().VisitTemplateArgument(arg))
return false;
@@ -416,7 +409,7 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
}
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc& argloc) {
- ASTNode node(&argloc, *GlobalSourceManager());
+ ASTNode node(&argloc);
CurrentASTNodeUpdater canu(&current_ast_node_, &node);
if (!this->getDerived().VisitTemplateArgumentLoc(argloc))
return false;
@@ -451,7 +444,7 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
// etc.
string AnnotatedName(const string& name) const {
return (PrintableCurrentLoc() + ": (" +
- IntToString(current_ast_node_->depth()) + GetSymbolAnnotation() +
+ std::to_string(current_ast_node_->depth()) + GetSymbolAnnotation() +
(current_ast_node_->in_forward_declare_context() ?
", fwd decl" : "") +
") [ " + name + " ] ");
@@ -549,32 +542,10 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
//
// * CXXDestructorDecl: a destructor call for each non-POD field
// in the dtor's class, and each base type of that class.
- // * CXXConstructorDecl: a constructor call for each type/base
- // of the class that is not explicitly listed in an initializer.
// * 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.
-
- bool TraverseCXXConstructorDecl(clang::CXXConstructorDecl* decl) {
- if (!Base::TraverseCXXConstructorDecl(decl)) return false;
- if (CanIgnoreCurrentASTNode()) return true;
- // We only care about classes that are actually defined.
- if (!decl || !decl->isThisDeclarationADefinition()) return true;
-
- // RAV's TraverseCXXConstructorDecl already handles
- // explicitly-written initializers, so we just want the rest.
- for (CXXConstructorDecl::init_const_iterator it = decl->init_begin();
- it != decl->init_end(); ++it) {
- const CXXCtorInitializer* init = *it;
- if (!init->isWritten()) {
- if (!this->getDerived().TraverseStmt(init->getInit()))
- return false;
- }
- }
- return true;
- }
-
bool TraverseCXXDestructorDecl(clang::CXXDestructorDecl* decl) {
if (!Base::TraverseCXXDestructorDecl(decl)) return false;
if (CanIgnoreCurrentASTNode()) return true;
@@ -655,70 +626,21 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
sema.PerformPendingInstantiations();
}
- // clang doesn't bother to set a TypeSourceInfo for implicit
- // methods, since, well, they don't have a location. But
- // RecursiveASTVisitor crashes without one, so when we lie and say
- // we're not implicit, we have to lie and give a location as well.
- // (We give the null location.) This is a small memory leak.
- void SetTypeSourceInfoForImplicitMethodIfNeeded(FunctionDecl* decl) {
- if (decl->getTypeSourceInfo() == nullptr) {
- ASTContext& ctx = compiler_->getASTContext();
- decl->setTypeSourceInfo(ctx.getTrivialTypeSourceInfo(decl->getType()));
- }
- }
-
- // RAV.h's TraverseDecl() ignores implicit nodes, so we lie a bit.
- // TODO(csilvers): figure out a more principled way.
- bool TraverseImplicitDeclHelper(clang::FunctionDecl* decl) {
- CHECK_(decl->isImplicit() && "TraverseImplicitDecl is for implicit decls");
- decl->setImplicit(false);
- SetTypeSourceInfoForImplicitMethodIfNeeded(decl);
- bool retval = this->getDerived().TraverseDecl(decl);
- decl->setImplicit(true);
- return retval;
- }
-
// Handle implicit methods that otherwise wouldn't be seen by RAV.
bool TraverseCXXRecordDecl(clang::CXXRecordDecl* decl) {
- if (!Base::TraverseCXXRecordDecl(decl)) return false;
if (CanIgnoreCurrentASTNode()) return true;
// We only care about classes that are actually defined.
- if (!decl || !decl->isThisDeclarationADefinition()) return true;
-
- InstantiateImplicitMethods(decl);
-
- // Check to see if there are any implicit constructors. Can be
- // several: implicit default constructor, implicit copy constructor.
- for (CXXRecordDecl::ctor_iterator it = decl->ctor_begin();
- it != decl->ctor_end(); ++it) {
- CXXConstructorDecl* ctor = *it;
- if (ctor->isImplicit() && !ctor->isDeleted()) {
- if (!TraverseImplicitDeclHelper(ctor))
- return false;
- }
+ if (decl && decl->isThisDeclarationADefinition()) {
+ InstantiateImplicitMethods(decl);
}
- // Check the (single) destructor.
- bool has_implicit_declared_destructor =
- (!decl->needsImplicitDestructor() &&
- !decl->hasUserDeclaredDestructor());
- if (has_implicit_declared_destructor) {
- if (!TraverseImplicitDeclHelper(decl->getDestructor()))
- return false;
- }
-
- // Check copy and move assignment operators.
- for (CXXRecordDecl::method_iterator it = decl->method_begin();
- it != decl->method_end(); ++it) {
- bool is_assignment_operator =
- it->isCopyAssignmentOperator() || it->isMoveAssignmentOperator();
- if (is_assignment_operator && it->isImplicit()) {
- if (!TraverseImplicitDeclHelper(*it))
- return false;
- }
- }
+ return Base::TraverseCXXRecordDecl(decl);
+ }
- return true;
+ bool TraverseClassTemplateSpecializationDecl(
+ clang::ClassTemplateSpecializationDecl* decl) {
+ if (!Base::TraverseClassTemplateSpecializationDecl(decl)) return false;
+ return TraverseCXXRecordDecl(decl);
}
//------------------------------------------------------------
@@ -842,8 +764,6 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
!IsCXXConstructExprInInitializer(current_ast_node()) &&
!IsCXXConstructExprInNewExpr(current_ast_node());
if (will_call_implicit_destructor_on_leaving_scope) {
- // Create the destructor if it hasn't been lazily created yet.
- InstantiateImplicitMethods(expr->getConstructor()->getParent());
if (const CXXDestructorDecl* dtor_decl = GetSiblingDestructorFor(expr)) {
if (!this->getDerived().TraverseImplicitDestructorCall(
const_cast<CXXDestructorDecl*>(dtor_decl), GetTypeOf(expr)))
@@ -858,7 +778,6 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
if (CanIgnoreCurrentASTNode()) return true;
// In this case, we *know* we're responsible for destruction as well.
- InstantiateImplicitMethods(expr->getConstructor()->getParent());
CXXConstructorDecl* ctor_decl = expr->getConstructor();
CXXDestructorDecl* dtor_decl =
const_cast<CXXDestructorDecl*>(GetSiblingDestructorFor(expr));
@@ -926,6 +845,12 @@ class BaseAstVisitor : public RecursiveASTVisitor<Derived> {
return true;
}
+ /// Return whether this visitor should recurse into implicit
+ /// code, e.g., implicit constructors and destructors.
+ bool shouldVisitImplicitCode() const {
+ return true;
+ }
+
protected:
CompilerInstance* compiler() { return compiler_; }
@@ -1048,15 +973,7 @@ class AstFlattenerVisitor : public BaseAstVisitor<AstFlattenerVisitor> {
CHECK_(seen_nodes_.empty() && "Nodes should be clear before GetNodesBelow");
NodeSet* node_set = &nodeset_decl_cache_[decl];
if (node_set->empty()) {
- if (decl->isImplicit()) {
- // TODO: For now, it is only working for functions. Check if it could
- // make sense for other implicit decls too (e.g. BuiltinTemplateDecl)
- if (FunctionDecl* func = DynCastFrom(decl)) {
- TraverseImplicitDeclHelper(func);
- }
- } else {
- TraverseDecl(decl);
- }
+ TraverseDecl(decl);
swap(*node_set, seen_nodes_); // move the seen_nodes_ into the cache
}
return *node_set; // returns the cache entry
@@ -1242,13 +1159,19 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
using Base::PrintableCurrentLoc;
using Base::current_ast_node;
+ enum class IgnoreKind {
+ ForUse,
+ ForExpansion,
+ };
+
//------------------------------------------------------------
// Pure virtual methods that a subclass must implement.
// Returns true if we are not interested in iwyu information for the
// given type, where the type is *not* the current AST node.
// TODO(csilvers): check we're calling this consistent with its API.
- virtual bool CanIgnoreType(const Type* type) const = 0;
+ virtual bool CanIgnoreType(const Type* type,
+ IgnoreKind = IgnoreKind::ForUse) const = 0;
// Returns true if we are not interested in doing an iwyu check on
// the given decl, where the decl is *not* the current AST node.
@@ -1649,7 +1572,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
// Sometimes a shadow decl comes between us and the 'real' decl.
if (const UsingShadowDecl* shadow_decl = DynCastFrom(used_decl))
target_decl = shadow_decl->getTargetDecl();
-
+
// Map private decls like __normal_iterator to their public counterpart.
target_decl = MapPrivateDeclToPublicDecl(target_decl);
if (CanIgnoreDecl(target_decl))
@@ -1677,7 +1600,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
// instead? We can call it "Use what you use". :-)
// TODO(csilvers): check for using statements and namespace aliases too.
if (const UsingDecl* using_decl
- = GetUsingDeclarationOf(used_decl,
+ = GetUsingDeclarationOf(used_decl,
GetDeclContext(current_ast_node()))) {
preprocessor_info().FileInfoFor(used_in)->ReportUsingDeclUse(
used_loc, using_decl, use_flags, "(for using decl)");
@@ -1736,7 +1659,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
// If we're a use that depends on a using declaration, make sure
// we #include the file with the using declaration.
if (const UsingDecl* using_decl
- = GetUsingDeclarationOf(used_decl,
+ = GetUsingDeclarationOf(used_decl,
GetDeclContext(current_ast_node()))) {
preprocessor_info().FileInfoFor(used_in)->ReportUsingDeclUse(
used_loc, using_decl, ComputeUseFlags(current_ast_node()),
@@ -2269,6 +2192,8 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
bool VisitUnaryExprOrTypeTraitExpr(clang::UnaryExprOrTypeTraitExpr* expr) {
if (CanIgnoreCurrentASTNode()) return true;
+ current_ast_node()->set_in_forward_declare_context(false);
+
// Calling sizeof on a reference-to-X is the same as calling it on X.
// If sizeof() takes a type, this is easy to check. If sizeof()
// takes an expr, it's hard to tell -- GetTypeOf(expr) 'sees through'
@@ -2675,9 +2600,7 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
// return false;
// Read past elaborations like 'class' keyword or namespaces.
- while (ast_node->ParentIsA<ElaboratedType>()) {
- ast_node = ast_node->parent();
- }
+ ast_node = MostElaboratedAncestor(ast_node);
// Now there are two options: either we have a type or we have a declaration
// involving a type.
@@ -2891,10 +2814,22 @@ class InstantiatedTemplateVisitor
string GetSymbolAnnotation() const override { return " in tpl"; }
- bool CanIgnoreType(const Type* type) const override {
- if (!IsTypeInteresting(type) || !IsKnownTemplateParam(type))
+ bool CanIgnoreType(const Type* type, IgnoreKind ignore_kind =
+ IgnoreKind::ForUse) const override {
+ if (!IsTypeInteresting(type))
return true;
+ switch (ignore_kind) {
+ case IgnoreKind::ForUse:
+ if (!IsKnownTemplateParam(type))
+ return true;
+ break;
+ case IgnoreKind::ForExpansion:
+ if (!InvolvesKnownTemplateParam(type))
+ return true;
+ break;
+ }
+
// If we're a default template argument, we should ignore the type
// if the template author intend-to-provide it, but otherwise we
// should not ignore it -- the caller is responsible for the type.
@@ -2911,7 +2846,6 @@ class InstantiatedTemplateVisitor
return IsDefaultTemplateParameter(type) && IsProvidedByTemplate(type);
}
-
bool IsTypeInteresting(const Type* type) const {
// We only care about types that would have been dependent in the
// uninstantiated template: that is, SubstTemplateTypeParmType types
@@ -2928,6 +2862,11 @@ class InstantiatedTemplateVisitor
return ContainsKey(resugar_map_, type);
}
+ bool InvolvesKnownTemplateParam(const Type* type) const {
+ return InvolvesTypeForWhich(
+ type, [&](const Type* type) { return IsKnownTemplateParam(type); });
+ }
+
// We ignore function calls in nodes_to_ignore_, which were already
// handled by the template-as-written, and function names that we
// are not responsible for because the template code is (for
@@ -3044,7 +2983,8 @@ class InstantiatedTemplateVisitor
bool TraverseSubstTemplateTypeParmTypeHelper(
const clang::SubstTemplateTypeParmType* type) {
- if (CanIgnoreCurrentASTNode() || CanIgnoreType(type))
+ if (CanIgnoreCurrentASTNode() ||
+ CanIgnoreType(type, IgnoreKind::ForExpansion))
return true;
const Type* actual_type = ResugarType(type);
@@ -3069,21 +3009,9 @@ class InstantiatedTemplateVisitor
return TraverseSubstTemplateTypeParmTypeHelper(typeloc.getTypePtr());
}
- // These do the actual work of finding the types to return. Our
- // task is made easier since (at least in theory), every time we
- // instantiate a template type, the instantiation has type
- // SubstTemplateTypeParmTypeLoc in the AST tree.
- bool VisitSubstTemplateTypeParmType(clang::SubstTemplateTypeParmType* type) {
- if (CanIgnoreCurrentASTNode() || CanIgnoreType(type))
- return true;
-
- // Figure out how this type was actually written. clang always
- // canonicalizes SubstTemplateTypeParmType, losing typedef info, etc.
- const Type* actual_type = ResugarType(type);
- CHECK_(actual_type && "If !CanIgnoreType(), we should be resugar-able");
-
- // TODO(csilvers): whenever we report a type use here, we want to
- // do an iwyu check on this type (to see if sub-types are used).
+ // Check whether a use of a template parameter is a full use.
+ bool IsTemplateTypeParmUseFullUse(const Type* type) {
+ const ASTNode* node = MostElaboratedAncestor(current_ast_node());
// If we're a nested-name-specifier class (the Foo in Foo::bar),
// we need our full type info no matter what the context (even if
@@ -3091,9 +3019,8 @@ class InstantiatedTemplateVisitor
// TODO(csilvers): consider encoding this logic via
// in_forward_declare_context. I think this will require changing
// in_forward_declare_context to yes/no/maybe.
- if (current_ast_node()->ParentIsA<NestedNameSpecifier>()) {
- ReportTypeUse(CurrentLoc(), actual_type);
- return Base::VisitSubstTemplateTypeParmType(type);
+ if (node->ParentIsA<NestedNameSpecifier>()) {
+ return true;
}
// If we're inside a typedef, we don't need our full type info --
@@ -3109,45 +3036,140 @@ class InstantiatedTemplateVisitor
// to require it as well. TODO(csilvers): this doesn't really
// make any sense. Who figures out we need the full type if
// you do 'Foo<MyClass>::value_type m;'?
- for (const ASTNode* ast_node = current_ast_node();
- ast_node != caller_ast_node_; ast_node = ast_node->parent()) {
- if (ast_node->IsA<TypedefNameDecl>())
- return Base::VisitSubstTemplateTypeParmType(type);
+ for (const ASTNode* ast_node = node; ast_node != caller_ast_node_;
+ ast_node = ast_node->parent()) {
+ if (ast_node->IsA<TypedefNameDecl>()) {
+ return false;
+ }
+ if (ast_node->IsA<TemplateSpecializationType>()) {
+ // If we hit a template specialization node before the typedef then we
+ // probably still need a full-use, so stop looking.
+ break;
+ }
}
// sizeof(a reference type) is the same as sizeof(underlying type).
// We have to handle that specially here, or else we'll say the
// reference is forward-declarable, below.
- if (current_ast_node()->ParentIsA<UnaryExprOrTypeTraitExpr>() &&
- isa<ReferenceType>(actual_type)) {
- const ReferenceType* actual_reftype = cast<ReferenceType>(actual_type);
- ReportTypeUse(CurrentLoc(),
- actual_reftype->getPointeeTypeAsWritten().getTypePtr());
- return Base::VisitSubstTemplateTypeParmType(type);
+ if (node->ParentIsA<UnaryExprOrTypeTraitExpr>() &&
+ isa<ReferenceType>(type)) {
+ return true;
}
// If we're used in a forward-declare context (MyFunc<T>() { T* t; }),
// or are ourselves a pointer type (MyFunc<Myclass*>()),
// we don't need to do anything: we're fine being forward-declared.
- if (current_ast_node()->in_forward_declare_context())
- return Base::VisitSubstTemplateTypeParmType(type);
+ if (node->in_forward_declare_context())
+ return false;
- if (current_ast_node()->ParentIsA<PointerType>() ||
- current_ast_node()->ParentIsA<LValueReferenceType>() ||
- IsPointerOrReferenceAsWritten(actual_type))
- return Base::VisitSubstTemplateTypeParmType(type);
+ if (node->ParentIsA<PointerType>() ||
+ node->ParentIsA<LValueReferenceType>() ||
+ IsPointerOrReferenceAsWritten(type))
+ return false;
- // We attribute all uses in an instantiated template to the
- // template's caller.
- ReportTypeUse(caller_loc(), actual_type);
+ return true;
+ }
- // Also report all previous explicit instantiations (declarations and
- // definitions) as uses of the caller's location.
- ReportExplicitInstantiations(actual_type);
+ // This helper is called on every use of a template argument type in an
+ // instantiated template. Its goal is to determine whether that use should
+ // constitute a full-use by the template caller, and perform other necessary
+ // bookkeeping.
+ void AnalyzeTemplateTypeParmUse(const Type* type) {
+ const ASTNode* node = MostElaboratedAncestor(current_ast_node());
+
+ // If the immediate parent node is a typedef, then register the new type as
+ // a new name for the template argument.
+ if (const TypedefNameDecl* typedef_decl =
+ node->GetParentAs<TypedefNameDecl>()) {
+ const Type* typedef_type = typedef_decl->getTypeForDecl();
+ VERRS(6) << "Registering " << PrintableType(typedef_type)
+ << " as an alias for " << PrintableType(type)
+ << " in the context of this instantiation\n";
+ template_argument_aliases_.emplace(typedef_type, type);
+ }
+
+ // Figure out how this type was actually written. clang always
+ // canonicalizes SubstTemplateTypeParmType, losing typedef info, etc.
+ const Type* actual_type = ResugarType(type);
+ CHECK_(actual_type &&
+ "Type passed to AnalyzeTemplateTypeParmUse should be resugar-able");
+
+ VERRS(6) << "AnalyzeTemplateTypeParmUse: type = " << PrintableType(type)
+ << ", actual_type = " << PrintableType(actual_type) << '\n';
+
+ if (!IsTemplateTypeParmUseFullUse(actual_type)) {
+ // Non-full uses will already have been reported when they were used as
+ // template arguments, so nothing to do here.
+ return;
+ }
+
+ if (isa<ReferenceType>(actual_type)) {
+ // If the argument type is a reference type, then we actually care about
+ // the referred-to type.
+ const ReferenceType* actual_reftype = cast<ReferenceType>(actual_type);
+ type = actual_reftype->getPointeeTypeAsWritten().getTypePtr();
+ }
+
+ // At this point we know we are looking at a full-use of type. However,
+ // there are still two cases. If this is a template param that was written
+ // by the user, then we report a use of it. However, it might also be a
+ // parameter of some implementation detail template which just happens top
+ // involve the user's template parameter somehow. In this case, we need to
+ // recursively explore the instantiation of *that* template to see if the
+ // user's type is full-used therein.
+
+ if (IsKnownTemplateParam(type)) {
+ // We attribute all uses in an instantiated template to the
+ // template's caller.
+ ReportTypeUse(caller_loc(), type);
+
+ // Also report all previous explicit instantiations (declarations and
+ // definitions) as uses of the caller's location.
+ ReportExplicitInstantiations(type);
+ } else {
+ const Decl* decl = TypeToDeclAsWritten(type);
+ if (const auto* cts_decl =
+ dyn_cast<ClassTemplateSpecializationDecl>(decl)) {
+ if (ContainsKey(traversed_decls_, decl))
+ return; // avoid recursion & repetition
+ traversed_decls_.insert(decl);
+
+ VERRS(6)
+ << "Recursively traversing " << PrintableDecl(cts_decl)
+ << " which was full-used and involves a known template param\n";
+ TraverseDecl(const_cast<ClassTemplateSpecializationDecl*>(cts_decl));
+ }
+ }
+ }
+
+ // Our task is made easier since (at least in theory), every time we
+ // instantiate a template type, the instantiation has type
+ // SubstTemplateTypeParmTypeLoc in the AST tree.
+ bool VisitSubstTemplateTypeParmType(clang::SubstTemplateTypeParmType* type) {
+ if (CanIgnoreCurrentASTNode() ||
+ CanIgnoreType(type, IgnoreKind::ForExpansion))
+ return true;
+
+ AnalyzeTemplateTypeParmUse(type);
return Base::VisitSubstTemplateTypeParmType(type);
}
+ bool VisitTypedefType(clang::TypedefType* type) {
+ if (CanIgnoreCurrentASTNode())
+ return true;
+
+ // Typedefs of template arguments require special handling to ensure that
+ // we record full-uses of those arguments where appropriate. Those
+ // typedefs are stored in the template_argument_aliases_ map.
+ if (const Type* template_arg_type =
+ GetOrDefault(template_argument_aliases_, type, nullptr)) {
+ AnalyzeTemplateTypeParmUse(template_arg_type);
+ }
+
+ return Base::VisitTypedefType(type);
+ }
+
bool VisitTemplateSpecializationType(TemplateSpecializationType* type) {
if (CanIgnoreCurrentASTNode())
return true;
@@ -3208,6 +3230,7 @@ class InstantiatedTemplateVisitor
void Clear() {
caller_ast_node_ = nullptr;
resugar_map_.clear();
+ template_argument_aliases_.clear();
traversed_decls_.clear();
nodes_to_ignore_.clear();
cache_storers_.clear();
@@ -3315,15 +3338,9 @@ class InstantiatedTemplateVisitor
nodes_to_ignore_.AddAll(nodeset_getter.GetNodesBelow(daw));
}
- // We need to iterate over the function. We do so even if it's
- // an implicit function.
- if (fn_decl->isImplicit()) {
- if (!TraverseImplicitDeclHelper(const_cast<FunctionDecl*>(fn_decl)))
- return false;
- } else {
- if (!TraverseDecl(const_cast<FunctionDecl*>(fn_decl)))
- return false;
- }
+ // We need to iterate over the function.
+ if (!TraverseDecl(const_cast<FunctionDecl*>(fn_decl)))
+ return false;
// If we're a constructor, we also need to construct the entire class,
// even typedefs that aren't used at construct time. Try compiling
@@ -3537,6 +3554,12 @@ class InstantiatedTemplateVisitor
// template-caller may or may not be responsible for.
map<const Type*, const Type*> resugar_map_;
+ // When we see a full-use of a template argument we need to assign that full
+ // use to the template-caller. Sometimes those uses are hidden behind
+ // type aliases (typedefs). This maps those aliases to the underlying
+ // template arguments.
+ map<const Type*, const Type*> template_argument_aliases_;
+
// Used to avoid recursion in the *Helper() methods.
set<const Decl*> traversed_decls_;
@@ -3598,7 +3621,7 @@ class IwyuAstConsumer
string GetSymbolAnnotation() const override { return ""; }
// We are interested in all types for iwyu checking.
- bool CanIgnoreType(const Type* type) const override {
+ bool CanIgnoreType(const Type* type, IgnoreKind) const override {
return type == nullptr;
}
@@ -3761,6 +3784,10 @@ class IwyuAstConsumer
bool VisitTagDecl(clang::TagDecl* decl) {
if (CanIgnoreCurrentASTNode()) return true;
+ // Skip the injected class name.
+ if (decl->isImplicit())
+ return Base::VisitTagDecl(decl);
+
if (IsForwardDecl(decl)) {
// If we're a templated class, make sure we add the whole template.
const NamedDecl* decl_to_fwd_declare = decl;
@@ -3940,7 +3967,7 @@ class IwyuAstConsumer
if (const TemplateSpecializationType* arg_tmpl = DynCastFrom(arg_type)) {
// Special case: We are instantiating the type in the context of an
// expression. Need to push the type to the AST stack explicitly.
- ASTNode node(arg_tmpl, *GlobalSourceManager());
+ ASTNode node(arg_tmpl);
node.SetParent(current_ast_node());
instantiated_template_visitor_.ScanInstantiatedType(
diff --git a/iwyu_ast_util.cc b/iwyu_ast_util.cc
index b97396e..1d37a17 100644
--- a/iwyu_ast_util.cc
+++ b/iwyu_ast_util.cc
@@ -20,12 +20,12 @@
#include "iwyu_path_util.h"
#include "iwyu_port.h" // for CHECK_
#include "iwyu_stl_util.h"
-#include "iwyu_string_util.h"
#include "iwyu_verrs.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTDumper.h"
#include "clang/AST/CanonicalType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
@@ -47,6 +47,7 @@ namespace clang {
class FileEntry;
} // namespace clang
+using clang::ASTDumper;
using clang::BlockPointerType;
using clang::CXXConstructExpr;
using clang::CXXConstructorDecl;
@@ -110,6 +111,7 @@ using clang::TemplateSpecializationKind;
using clang::TemplateSpecializationType;
using clang::TranslationUnitDecl;
using clang::Type;
+using clang::TypeAliasTemplateDecl;
using clang::TypeDecl;
using clang::TypeLoc;
using clang::TypedefNameDecl;
@@ -122,6 +124,7 @@ using llvm::ArrayRef;
using llvm::PointerUnion;
using llvm::cast;
using llvm::dyn_cast;
+using llvm::dyn_cast_or_null;
using llvm::errs;
using llvm::isa;
using llvm::raw_string_ostream;
@@ -183,13 +186,12 @@ SourceLocation ASTNode::GetLocation() const {
// locations are in a different file, then we're uncertain of our
// own location. Return an invalid location.
if (retval.isValid()) {
- FullSourceLoc full_loc(retval, source_manager_);
+ clang::SourceManager& sm = *GlobalSourceManager();
+ FullSourceLoc full_loc(retval, sm);
const FileEntry* spelling_file =
- source_manager_.getFileEntryForID(
- source_manager_.getFileID(full_loc.getSpellingLoc()));
+ sm.getFileEntryForID(sm.getFileID(full_loc.getSpellingLoc()));
const FileEntry* instantiation_file =
- source_manager_.getFileEntryForID(
- source_manager_.getFileID(full_loc.getExpansionLoc()));
+ sm.getFileEntryForID(sm.getFileID(full_loc.getExpansionLoc()));
if (spelling_file != instantiation_file)
return SourceLocation();
}
@@ -233,6 +235,14 @@ bool IsElaborationNode(const ASTNode* ast_node) {
return elaborated_type && elaborated_type->getKeyword() != clang::ETK_None;
}
+const ASTNode* MostElaboratedAncestor(const ASTNode* ast_node) {
+ // Read past elaborations like 'class' keyword or namespaces.
+ while (ast_node->ParentIsA<ElaboratedType>()) {
+ ast_node = ast_node->parent();
+ }
+ return ast_node;
+}
+
bool IsQualifiedNameNode(const ASTNode* ast_node) {
if (ast_node == nullptr)
return false;
@@ -441,12 +451,14 @@ string PrintableDecl(const Decl* decl, bool terse/*=true*/) {
string PrintableStmt(const Stmt* stmt) {
std::string buffer;
raw_string_ostream ostream(buffer);
- stmt->dump(ostream, *GlobalSourceManager());
+ ASTDumper dumper(ostream, /*ShowColors=*/false);
+ dumper.Visit(stmt);
return ostream.str();
}
void PrintStmt(const Stmt* stmt) {
- stmt->dump(*GlobalSourceManager()); // This prints to errs().
+ ASTDumper dumper(llvm::errs(), /*ShowColors=*/false);
+ dumper.Visit(stmt);
}
string PrintableType(const Type* type) {
@@ -596,14 +608,14 @@ bool HasImplicitConversionCtor(const CXXRecordDecl* cxx_class) {
}
// C++ [class.virtual]p8:
-// If the return type of D::f differs from the return type of B::f, the
+// If the return type of D::f differs from the return type of B::f, the
// class type in the return type of D::f shall be complete at the point of
// declaration of D::f or shall be the class type D.
bool HasCovariantReturnType(const CXXMethodDecl* method_decl) {
QualType derived_return_type = method_decl->getReturnType();
for (CXXMethodDecl::method_iterator
- it = method_decl->begin_overridden_methods();
+ it = method_decl->begin_overridden_methods();
it != method_decl->end_overridden_methods(); ++it) {
// There are further constraints on covariant return types as such
// (e.g. parents must be related, derived override must have return type
@@ -936,29 +948,6 @@ const NamedDecl* GetDefinitionAsWritten(const NamedDecl* decl) {
return decl;
}
-bool IsDefaultNewOrDelete(const FunctionDecl* decl,
- const string& decl_loc_as_quoted_include) {
- // Clang will report <new> as the location of the default new and
- // delete operators if <new> is included. Otherwise, it reports the
- // (fake) file "<built-in>".
- if (decl_loc_as_quoted_include != "<new>" &&
- !IsBuiltinFile(GetFileEntry(decl)))
- return false;
-
- const string decl_name = decl->getNameAsString();
- if (!StartsWith(decl_name, "operator new") &&
- !StartsWith(decl_name, "operator delete"))
- return false;
-
- // Placement-new/delete has 2 args, second is void*. The only other
- // 2-arg overloads of new/delete in <new> take a const nothrow_t&.
- if (decl->getNumParams() == 2 &&
- !decl->getParamDecl(1)->getType().isConstQualified())
- return false;
-
- return true;
-}
-
bool IsFriendDecl(const Decl* decl) {
// For 'template<...> friend class T', the decl will just be 'class T'.
// We need to go 'up' a level to check friendship in the right place.
@@ -1170,6 +1159,26 @@ const Type* RemoveSubstTemplateTypeParm(const Type* type) {
return type;
}
+bool InvolvesTypeForWhich(const Type* type,
+ std::function<bool(const Type*)> pred) {
+ type = RemoveSubstTemplateTypeParm(type);
+ if (pred(type))
+ return true;
+ const Decl* decl = TypeToDeclAsWritten(type);
+ if (const auto* cts_decl =
+ dyn_cast_or_null<ClassTemplateSpecializationDecl>(decl)) {
+ const TemplateArgumentList& tpl_args = cts_decl->getTemplateArgs();
+ for (const TemplateArgument& tpl_arg : tpl_args.asArray()) {
+ if (const Type* arg_type = GetTemplateArgAsType(tpl_arg)) {
+ if (InvolvesTypeForWhich(arg_type, pred)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
bool IsPointerOrReferenceAsWritten(const Type* type) {
type = RemoveElaboration(type);
return isa<PointerType>(type) || isa<LValueReferenceType>(type);
@@ -1207,7 +1216,7 @@ const Type* RemovePointersAndReferences(const Type* type) {
return type;
}
-const NamedDecl* TypeToDeclAsWritten(const Type* type) {
+static const NamedDecl* TypeToDeclImpl(const Type* type, bool as_written) {
// Get past all the 'class' and 'struct' prefixes, and namespaces.
type = RemoveElaboration(type);
@@ -1222,20 +1231,29 @@ const NamedDecl* TypeToDeclAsWritten(const Type* type) {
// to keep typedefs as typedefs, so we do the record check last.
// We use getAs<> when we can -- unfortunately, it only exists
// for a few types so far.
+ const TemplateSpecializationType* template_spec_type = DynCastFrom(type);
+ const TemplateDecl* template_decl =
+ template_spec_type
+ ? template_spec_type->getTemplateName().getAsTemplateDecl()
+ : nullptr;
+
if (const TypedefType* typedef_type = DynCastFrom(type)) {
return typedef_type->getDecl();
} else if (const InjectedClassNameType* icn_type
= type->getAs<InjectedClassNameType>()) {
return icn_type->getDecl();
+ } else if (as_written && template_decl &&
+ isa<TypeAliasTemplateDecl>(template_decl)) {
+ // A template type alias
+ return template_decl;
} else if (const RecordType* record_type
= type->getAs<RecordType>()) {
return record_type->getDecl();
} else if (const TagType* tag_type = DynCastFrom(type)) {
return tag_type->getDecl(); // probably just enums
- } else if (const TemplateSpecializationType* template_spec_type
- = DynCastFrom(type)) {
+ } else if (template_decl) {
// A non-concrete template class, such as 'Myclass<T>'
- return template_spec_type->getTemplateName().getAsTemplateDecl();
+ return template_decl;
} else if (const FunctionType* function_type = DynCastFrom(type)) {
// TODO(csilvers): is it possible to map from fn type to fn decl?
(void)function_type;
@@ -1245,6 +1263,14 @@ const NamedDecl* TypeToDeclAsWritten(const Type* type) {
}
}
+const NamedDecl* TypeToDeclAsWritten(const Type* type) {
+ return TypeToDeclImpl(type, /*as_written=*/true);
+}
+
+const NamedDecl* TypeToDeclForContent(const Type* type) {
+ return TypeToDeclImpl(type, /*as_written=*/false);
+}
+
const Type* RemoveReferenceAsWritten(const Type* type) {
if (const LValueReferenceType* ref_type = DynCastFrom(type))
return ref_type->getPointeeType().getTypePtr();
@@ -1280,13 +1306,20 @@ GetTplTypeResugarMapForClassNoComponentTypes(const clang::Type* type) {
if (!tpl_spec_type)
return retval;
- // Get the list of template args that apply to the decls.
+ // Pull the template arguments out of the specialization type. If this is
+ // a ClassTemplateSpecializationDecl specifically, we want to
+ // get the arguments therefrom to correctly handle default arguments.
+ const TemplateArgument* tpl_args = tpl_spec_type->getArgs();
+ unsigned num_args = tpl_spec_type->getNumArgs();
+
const NamedDecl* decl = TypeToDeclAsWritten(tpl_spec_type);
- const ClassTemplateSpecializationDecl* tpl_decl = DynCastFrom(decl);
- if (!tpl_decl) // probably because tpl_spec_type is a dependent type
- return retval;
- const TemplateArgumentList& tpl_args
- = tpl_decl->getTemplateInstantiationArgs();
+ const auto* cls_tpl_decl = dyn_cast<ClassTemplateSpecializationDecl>(decl);
+ if (cls_tpl_decl) {
+ const TemplateArgumentList& tpl_arg_list =
+ cls_tpl_decl->getTemplateInstantiationArgs();
+ tpl_args = tpl_arg_list.data();
+ num_args = tpl_arg_list.size();
+ }
// TemplateSpecializationType only includes explicitly specified
// types in its args list, so we start with that. Note that an
@@ -1302,12 +1335,13 @@ GetTplTypeResugarMapForClassNoComponentTypes(const clang::Type* type) {
// (the latter are all desugared). If there's a match, update
// the mapping.
for (const Type* type : arg_components) {
- for (unsigned i = 0; i < tpl_args.size(); ++i) {
+ for (unsigned i = 0; i < num_args; ++i) {
if (const Type* arg_type = GetTemplateArgAsType(tpl_args[i])) {
if (GetCanonicalType(type) == arg_type) {
retval[arg_type] = type;
VERRS(6) << "Adding a template-class type of interest: "
- << PrintableType(type) << "\n";
+ << PrintableType(arg_type) << " -> " << PrintableType(type)
+ << "\n";
explicit_args.insert(i);
}
}
@@ -1316,7 +1350,7 @@ GetTplTypeResugarMapForClassNoComponentTypes(const clang::Type* type) {
}
// Now take a look at the args that were not filled explicitly.
- for (unsigned i = 0; i < tpl_args.size(); ++i) {
+ for (unsigned i = 0; i < num_args; ++i) {
if (ContainsKey(explicit_args, i))
continue;
if (const Type* arg_type = GetTemplateArgAsType(tpl_args[i])) {
diff --git a/iwyu_ast_util.h b/iwyu_ast_util.h
index f62437b..18ddf2d 100644
--- a/iwyu_ast_util.h
+++ b/iwyu_ast_util.h
@@ -40,7 +40,6 @@ class ClassTemplateDecl;
class Expr;
class FunctionDecl;
class NamedDecl;
-class SourceManager;
class TagDecl;
class TemplateDecl;
class TemplateName;
@@ -72,37 +71,33 @@ class ASTNode {
public:
// In each case, the caller owns the object, and must guarantee it
// lives for at least as long as the ASTNode object does.
- ASTNode(const clang::Decl* decl, const clang::SourceManager& sm)
+ ASTNode(const clang::Decl* decl)
: kind_(kDeclKind), as_decl_(decl),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
- ASTNode(const clang::Stmt* stmt, const clang::SourceManager& sm)
+ parent_(nullptr), in_fwd_decl_context_(false) { }
+ ASTNode(const clang::Stmt* stmt)
: kind_(kStmtKind), as_stmt_(stmt),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
- ASTNode(const clang::Type* type, const clang::SourceManager& sm)
+ parent_(nullptr), in_fwd_decl_context_(false) { }
+ ASTNode(const clang::Type* type)
: kind_(kTypeKind), as_type_(type),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
- ASTNode(const clang::TypeLoc* typeloc, const clang::SourceManager& sm)
+ parent_(nullptr), in_fwd_decl_context_(false) { }
+ ASTNode(const clang::TypeLoc* typeloc)
: kind_(kTypelocKind), as_typeloc_(typeloc),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
- ASTNode(const clang::NestedNameSpecifier* nns, const clang::SourceManager& sm)
+ parent_(nullptr), in_fwd_decl_context_(false) { }
+ ASTNode(const clang::NestedNameSpecifier* nns)
: kind_(kNNSKind), as_nns_(nns),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
- ASTNode(const clang::NestedNameSpecifierLoc* nnsloc,
- const clang::SourceManager& sm)
+ parent_(nullptr), in_fwd_decl_context_(false) { }
+ ASTNode(const clang::NestedNameSpecifierLoc* nnsloc)
: kind_(kNNSLocKind), as_nnsloc_(nnsloc),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
- ASTNode(const clang::TemplateName* template_name,
- const clang::SourceManager& sm)
+ parent_(nullptr), in_fwd_decl_context_(false) { }
+ ASTNode(const clang::TemplateName* template_name)
: kind_(kTemplateNameKind), as_template_name_(template_name),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
- ASTNode(const clang::TemplateArgument* template_arg,
- const clang::SourceManager& sm)
+ parent_(nullptr), in_fwd_decl_context_(false) { }
+ ASTNode(const clang::TemplateArgument* template_arg)
: kind_(kTemplateArgumentKind), as_template_arg_(template_arg),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
- ASTNode(const clang::TemplateArgumentLoc* template_argloc,
- const clang::SourceManager& sm)
+ parent_(nullptr), in_fwd_decl_context_(false) { }
+ ASTNode(const clang::TemplateArgumentLoc* template_argloc)
: kind_(kTemplateArgumentLocKind), as_template_argloc_(template_argloc),
- parent_(nullptr), in_fwd_decl_context_(false), source_manager_(sm) { }
+ parent_(nullptr), in_fwd_decl_context_(false) { }
// A 'forward-declare' context means some parent of us can be
// forward-declared, which means we can be too. e.g. in
@@ -327,7 +322,6 @@ class ASTNode {
};
const ASTNode* parent_;
bool in_fwd_decl_context_;
- const clang::SourceManager& source_manager_;
};
// --- Helper classes for ASTNode.
@@ -376,6 +370,11 @@ class CurrentASTNodeUpdater {
// uses ElaboratedType for namespaces ('ns::Foo myvar').
bool IsElaborationNode(const ASTNode* ast_node);
+// Walk up to parents of the given node so long as each parent is an
+// elaboration node (in the sense of IsElaborationNode).
+// Can expand from a node representing 'X' to e.g. 'struct X' or 'mylib::X'.
+const ASTNode* MostElaboratedAncestor(const ASTNode* ast_node);
+
// See if a given ast_node is a qualified name part of an ElaboratedType
// node (e.g. 'class ns::Foo x', 'class ::Global x' or 'class Outer::Inner x'.)
bool IsQualifiedNameNode(const ASTNode* ast_node);
@@ -584,12 +583,6 @@ const clang::NamedDecl* GetInstantiatedFromDecl(
// the original input.
const clang::NamedDecl* GetDefinitionAsWritten(const clang::NamedDecl* decl);
-// True if this decl is for default (not placement-new)
-// new/delete/new[]/delete[] from <new>. The second argument
-// is the quoted form of the file the decl comes from, e.g. '<new>'.
-bool IsDefaultNewOrDelete(const clang::FunctionDecl* decl,
- const string& decl_loc_as_quoted_include);
-
// Returns true if this decl is part of a friend decl.
bool IsFriendDecl(const clang::Decl* decl);
@@ -700,6 +693,11 @@ bool IsClassType(const clang::Type* type);
// However, vector<T> is *not* converted to vector<int>.
const clang::Type* RemoveSubstTemplateTypeParm(const clang::Type* type);
+// Returns true if any type involved (recursively examining template
+// arguments) satisfies the given predicate.
+bool InvolvesTypeForWhich(const clang::Type* type,
+ std::function<bool(const clang::Type*)> pred);
+
// Returns true if type is a pointer type (pointer or reference,
// looking through elaborations like 'class Foo*' (vs 'Foo*'),
// but *not* following typedefs (which is why we can't just use
@@ -744,6 +742,16 @@ const clang::Type* RemovePointersAndReferences(const clang::Type* type);
// this function returns nullptr.
const clang::NamedDecl* TypeToDeclAsWritten(const clang::Type* type);
+// This is similar to TypeToDeclAsWritten, but in this case we are less
+// interested in how the type was written; we want the Decl which we can
+// explore the contents of, for example to determine which of its template
+// arguments are used in a manner that constitutes a full use.
+//
+// The difference arises particularly for type aliases, where
+// TypeToDeclAsWritten returns the Decl for the alias, whereas
+// TypeToDeclForContent returns the underlying aliased Decl.
+const clang::NamedDecl* TypeToDeclForContent(const clang::Type* type);
+
// Returns true if it's possible to implicitly convert a value of a
// different type to 'type' via an implicit constructor.
bool HasImplicitConversionConstructor(const clang::Type* type);
diff --git a/iwyu_driver.cc b/iwyu_driver.cc
index bd16314..42fea35 100644
--- a/iwyu_driver.cc
+++ b/iwyu_driver.cc
@@ -79,7 +79,7 @@ std::string GetExecutablePath(const char *Argv0) {
}
const char *SaveStringInSet(std::set<std::string> &SavedStrings, StringRef S) {
- return SavedStrings.insert(S).first->c_str();
+ return SavedStrings.insert(S.str()).first->c_str();
}
void ExpandArgsFromBuf(const char *Arg,
diff --git a/iwyu_globals.cc b/iwyu_globals.cc
index 0f58b4d..2699896 100644
--- a/iwyu_globals.cc
+++ b/iwyu_globals.cc
@@ -293,7 +293,7 @@ static vector<HeaderSearchPath> ComputeHeaderSearchPaths(
for (auto it = header_search->system_dir_begin();
it != header_search->system_dir_end(); ++it) {
if (const DirectoryEntry* entry = it->getDir()) {
- const string path = NormalizeDirPath(MakeAbsolutePath(entry->getName()));
+ const string path = NormalizeDirPath(MakeAbsolutePath(entry->getName().str()));
search_path_map[path] = HeaderSearchPath::kSystemPath;
}
}
@@ -303,7 +303,7 @@ static vector<HeaderSearchPath> ComputeHeaderSearchPaths(
// search_dir_begin()/end() includes both system and user paths.
// If it's a system path, it's already in the map, so everything
// new is a user path. The insert only 'takes' for new entries.
- const string path = NormalizeDirPath(MakeAbsolutePath(entry->getName()));
+ const string path = NormalizeDirPath(MakeAbsolutePath(entry->getName().str()));
search_path_map.insert(make_pair(path, HeaderSearchPath::kUserPath));
}
}
diff --git a/iwyu_globals.h b/iwyu_globals.h
index af40256..242cdf6 100644
--- a/iwyu_globals.h
+++ b/iwyu_globals.h
@@ -99,7 +99,7 @@ struct CommandlineFlags {
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 no_fwd_decls; // Disable forward declarations.
- bool quoted_includes_first; // Place quoted includes first in sort order.
+ bool quoted_includes_first; // Place quoted includes first in sort order.
bool cxx17ns; // -C: C++17 nested namespace syntax
};
diff --git a/iwyu_include_picker.cc b/iwyu_include_picker.cc
index 2bdf578..76f1de7 100644
--- a/iwyu_include_picker.cc
+++ b/iwyu_include_picker.cc
@@ -134,6 +134,7 @@ const IncludeMapEntry libc_symbol_map[] = {
{ "intptr_t", kPrivate, "<unistd.h>", kPublic },
{ "key_t", kPrivate, "<sys/types.h>", kPublic },
{ "key_t", kPrivate, "<sys/ipc.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, "<sys/ipc.h>", kPublic },
@@ -154,6 +155,7 @@ const IncludeMapEntry libc_symbol_map[] = {
{ "pid_t", kPrivate, "<termios.h>", kPublic },
{ "pid_t", kPrivate, "<time.h>", kPublic },
{ "pid_t", kPrivate, "<utmpx.h>", kPublic },
+ { "ptrdiff_t", kPrivate, "<stddef.h>", kPublic },
{ "sigset_t", kPrivate, "<signal.h>", kPublic },
{ "sigset_t", kPrivate, "<sys/epoll.h>", kPublic },
{ "sigset_t", kPrivate, "<sys/select.h>", kPublic },
@@ -182,6 +184,8 @@ const IncludeMapEntry libc_symbol_map[] = {
{ "uid_t", kPrivate, "<sys/stat.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.
@@ -349,6 +353,7 @@ const IncludeMapEntry libc_include_map[] = {
{ "<bits/syslog-path.h>", kPrivate, "<sys/syslog.h>", kPrivate },
{ "<bits/syslog.h>", kPrivate, "<sys/syslog.h>", kPrivate },
{ "<bits/termios.h>", kPrivate, "<termios.h>", kPublic },
+ { "<bits/time.h>", kPrivate, "<time.h>", kPublic },
{ "<bits/time.h>", kPrivate, "<sys/time.h>", kPublic },
{ "<bits/timerfd.h>", kPrivate, "<sys/timerfd.h>", kPublic },
{ "<bits/timex.h>", kPrivate, "<sys/timex.h>", kPublic },
@@ -1595,7 +1600,7 @@ void IncludePicker::AddMappingsFromFile(const string& filename,
// Add the path of the file we're currently processing
// to the search path. Allows refs to be relative to referrer.
- vector<string> extended_search_path =
+ vector<string> extended_search_path =
ExtendMappingFileSearchPath(search_path,
GetParentPath(absolute_path));
diff --git a/iwyu_include_picker.h b/iwyu_include_picker.h
index 69b6094..5a1f1f0 100644
--- a/iwyu_include_picker.h
+++ b/iwyu_include_picker.h
@@ -194,10 +194,10 @@ class IncludePicker {
// Adds a mapping from a one header to another, typically
// from a private to a public quoted include.
void AddIncludeMapping(
- const string& map_from, IncludeVisibility from_visibility,
+ const string& map_from, IncludeVisibility from_visibility,
const MappedInclude& map_to, IncludeVisibility to_visibility);
- // Adds a mapping from a a symbol to a quoted include. We use this to
+ // Adds a mapping from a a symbol to a quoted include. We use this to
// maintain mappings of documented types, e.g.
// For std::map<>, include <map>.
void AddSymbolMapping(
diff --git a/iwyu_lexer_utils.cc b/iwyu_lexer_utils.cc
index fcea2d2..648c9da 100644
--- a/iwyu_lexer_utils.cc
+++ b/iwyu_lexer_utils.cc
@@ -70,7 +70,7 @@ SourceLocation GetLocationAfter(
string GetIncludeNameAsWritten(
SourceLocation include_loc,
const CharacterDataGetterInterface& data_getter) {
- const string data = GetSourceTextUntilEndOfLine(include_loc, data_getter);
+ const string data = GetSourceTextUntilEndOfLine(include_loc, data_getter).str();
if (data.empty())
return data;
string::size_type endpos = string::npos;
diff --git a/iwyu_location_util.h b/iwyu_location_util.h
index 3892a42..6f8cf81 100644
--- a/iwyu_location_util.h
+++ b/iwyu_location_util.h
@@ -89,7 +89,7 @@ bool IsInScratchSpace(clang::SourceLocation loc);
inline string GetFilePath(const clang::FileEntry* file) {
return (IsBuiltinFile(file) ? "<built-in>" :
- NormalizeFilePath(file->getName()));
+ NormalizeFilePath(file->getName().str()));
}
//------------------------------------------------------------
diff --git a/iwyu_output.cc b/iwyu_output.cc
index ca14571..e102ccb 100644
--- a/iwyu_output.cc
+++ b/iwyu_output.cc
@@ -168,7 +168,7 @@ string GetKindName(const clang::TagDecl* tag_decl) {
if (const FakeNamedDecl* fake = FakeNamedDeclIfItIsOne(named_decl)) {
return fake->kind_name();
}
- return tag_decl->getKindName();
+ return tag_decl->getKindName().str();
}
string GetQualifiedNameAsString(const clang::NamedDecl* named_decl) {
@@ -584,8 +584,8 @@ void IwyuFileInfo::AddUsingDecl(const UsingDecl* using_decl) {
int start_linenum = GetLineNumber(GetInstantiationLoc(decl_lines.getBegin()));
int end_linenum = GetLineNumber(GetInstantiationLoc(decl_lines.getEnd()));
VERRS(6) << "Found using-decl: "
- << GetFilePath(file_) << ":"
- << to_string(start_linenum) << "-" << to_string(end_linenum) << ": "
+ << GetFilePath(file_) << ":"
+ << to_string(start_linenum) << "-" << to_string(end_linenum) << ": "
<< internal::PrintablePtr(using_decl)
<< internal::GetQualifiedNameAsString(using_decl) << "\n";
}
@@ -678,7 +678,7 @@ void IwyuFileInfo::ReportForwardDeclareUse(SourceLocation use_loc,
void IwyuFileInfo::ReportUsingDeclUse(SourceLocation use_loc,
const UsingDecl* using_decl,
UseFlags flags,
- const char* comment) {
+ const char* comment) {
// If accessing a symbol through a using decl in the same file that contains
// the using decl, we must mark the using decl as referenced. At the end of
// traversing the AST, we check to see if a using decl is unreferenced and
@@ -1242,10 +1242,14 @@ void ProcessFullUse(OneUse* use,
return;
}
// Special case for operators new/delete: Only treated as built-in if they
- // are the default, non-placement versions.
+ // are the default, non-placement versions. This is modelled in Clang as
+ // 'replaceable global allocation functions': the helper method returns true
+ // for anything but placement-new. Users of the 'std::nothrow' and
+ // 'std::align_val_t' overloads already need to spell these two symbols, so
+ // <new> will be required for them without us doing any magic for operator new
+ // itself.
if (const FunctionDecl* fn_decl = DynCastFrom(use->decl())) {
- const string dfn_file = GetFilePath(fn_decl);
- if (IsDefaultNewOrDelete(fn_decl, ConvertToQuotedInclude(dfn_file))) {
+ if (fn_decl->isReplaceableGlobalAllocationFunction()) {
VERRS(6) << "Ignoring use of " << use->symbol_name()
<< " (" << use->PrintableUseLoc() << "): built-in new/delete\n";
use->set_ignore_use();
@@ -1389,14 +1393,10 @@ void CalculateIwyuForForwardDeclareUse(
// If this record is defined in one of the desired_includes, mark that
// fact. Also if it's defined in one of the actual_includes.
- const NamedDecl* dfn = GetDefinitionForClass(use->decl());
- // If we are, ourselves, a template specialization, then the definition
- // we use is not the definition of the specialization (that's us), but
- // the definition of the template we're specializing.
- if (spec_decl && dfn == spec_decl)
- dfn = GetDefinitionForClass(spec_decl->getSpecializedTemplate());
bool dfn_is_in_desired_includes = false;
bool dfn_is_in_actual_includes = false;
+
+ const NamedDecl* dfn = GetDefinitionForClass(use->decl());
if (dfn) {
vector<string> headers
= GlobalIncludePicker().GetCandidateHeadersForFilepathIncludedFrom(
diff --git a/iwyu_output.h b/iwyu_output.h
index e274bc3..ac2c2bc 100644
--- a/iwyu_output.h
+++ b/iwyu_output.h
@@ -255,7 +255,7 @@ class IwyuFileInfo {
UseFlags flags, const char* comment);
// Called whenever a NamedDecl is accessed through a UsingDecl.
- // ie: using std::swap; swap(a, b);
+ // ie: using std::swap; swap(a, b);
void ReportUsingDeclUse(clang::SourceLocation use_loc,
const clang::UsingDecl* using_decl,
UseFlags flags, const char* comment);
diff --git a/iwyu_path_util.cc b/iwyu_path_util.cc
index ab4fc80..9987ea4 100644
--- a/iwyu_path_util.cc
+++ b/iwyu_path_util.cc
@@ -134,7 +134,7 @@ string NormalizeFilePath(const string& path) {
std::replace(normalized.begin(), normalized.end(), '\\', '/');
#endif
- return normalized.str();
+ return normalized.str().str();
}
string NormalizeDirPath(const string& path) {
@@ -154,14 +154,14 @@ string MakeAbsolutePath(const string& path) {
std::error_code error = llvm::sys::fs::make_absolute(absolute_path);
CHECK_(!error);
- return absolute_path.str();
+ return absolute_path.str().str();
}
string MakeAbsolutePath(const string& base_path, const string& relative_path) {
llvm::SmallString<128> absolute_path(base_path);
llvm::sys::path::append(absolute_path, relative_path);
- return absolute_path.str();
+ return absolute_path.str().str();
}
string GetParentPath(const string& path) {
diff --git a/iwyu_port.h b/iwyu_port.h
index a46d585..d890575 100644
--- a/iwyu_port.h
+++ b/iwyu_port.h
@@ -63,8 +63,6 @@ class OstreamVoidifier {
#if defined(_WIN32)
-#define snprintf _snprintf
-
#define NOMINMAX 1 // Prevent Windows headers from redefining min/max.
#include "Shlwapi.h" // for PathMatchSpecA
diff --git a/iwyu_preprocessor.cc b/iwyu_preprocessor.cc
index 58e7859..88b9314 100644
--- a/iwyu_preprocessor.cc
+++ b/iwyu_preprocessor.cc
@@ -313,7 +313,7 @@ void IwyuPreprocessorInfo::ProcessHeadernameDirectivesInFile(
break;
}
const string filename = GetSourceTextUntilEndOfLine(current_loc,
- DefaultDataGetter());
+ DefaultDataGetter()).str();
// Use "" or <> based on where the file lives.
string quoted_private_include;
if (IsSystemIncludeFile(GetFilePath(current_loc)))
@@ -332,7 +332,7 @@ void IwyuPreprocessorInfo::ProcessHeadernameDirectivesInFile(
}
string after_text = GetSourceTextUntilEndOfLine(current_loc,
- DefaultDataGetter());
+ DefaultDataGetter()).str();
const string::size_type close_brace_pos = after_text.find('}');
if (close_brace_pos == string::npos) {
Warn(current_loc, "@headername directive missing a closing brace");
diff --git a/iwyu_tool.py b/iwyu_tool.py
index 07210c9..a0768d6 100755
--- a/iwyu_tool.py
+++ b/iwyu_tool.py
@@ -267,6 +267,11 @@ class Process(object):
return cls(process, outfile)
+KNOWN_COMPILER_WRAPPERS=frozenset([
+ "ccache"
+])
+
+
class Invocation(object):
""" Holds arguments of an IWYU invocation. """
def __init__(self, command, cwd):
@@ -288,6 +293,10 @@ class Invocation(object):
else:
raise ValueError('Invalid compilation database entry: %s' % entry)
+ if command[0] in KNOWN_COMPILER_WRAPPERS:
+ # Remove the compiler wrapper from the command.
+ command = command[1:]
+
# Rewrite the compile command for IWYU
compile_command, compile_args = command[0], command[1:]
if is_msvc_driver(compile_command):
diff --git a/iwyu_tool_test.py b/iwyu_tool_test.py
index d0d8b59..d524dac 100755
--- a/iwyu_tool_test.py
+++ b/iwyu_tool_test.py
@@ -310,6 +310,19 @@ class CompilationDBTests(unittest.TestCase):
self.assertEqual('/c057f113f69311e990bf54a05050d914/foobar/Test.cpp',
entry['file'])
+ def test_unwrap_compile_command(self):
+ """ Wrapping compile commands should be unwrapped. """
+ compilation_db = {
+ 'directory': '/home/user/llvm/build',
+ "command": "ccache cc -c test.c"
+ }
+
+ invocation = iwyu_tool.Invocation.from_compile_command(compilation_db, [])
+
+ self.assertEqual(
+ invocation.command,
+ [iwyu_tool.IWYU_EXECUTABLE, '-c', 'test.c'])
+
if __name__ == '__main__':
unittest.main()
diff --git a/iwyu_version.h b/iwyu_version.h
index 2dada60..3cfbb5d 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.14"
+#define IWYU_VERSION_STRING "0.15"
#endif // INCLUDE_WHAT_YOU_USE_IWYU_VERSION_H_
diff --git a/more_tests/iwyu_include_picker_test.cc b/more_tests/iwyu_include_picker_test.cc
index be55316..8f2b72b 100644
--- a/more_tests/iwyu_include_picker_test.cc
+++ b/more_tests/iwyu_include_picker_test.cc
@@ -13,7 +13,6 @@
#include "iwyu_include_picker.h"
#include <stddef.h>
-#include <stdio.h>
#include <algorithm>
#include <string>
#include <vector>
@@ -36,27 +35,21 @@ namespace include_what_you_use {
namespace {
-string IntToString(int i) {
- char buf[64]; // big enough for any number
- snprintf(buf, sizeof(buf), "%d", i);
- return buf;
-}
-
// Returns a string representing the first element where actual (a vector),
// and expected (an array) differ, or "" if they're identical.
template <size_t kCount> string VectorDiff(const string (&expected)[kCount],
const vector<string>& actual) {
for (int i = 0; i < std::min(kCount, actual.size()); ++i) {
if (expected[i] != actual[i]) {
- return ("Differ at #" + IntToString(i) + ": expected=" + expected[i] +
+ return ("Differ at #" + std::to_string(i) + ": expected=" + expected[i] +
", actual=" + actual[i]);
}
}
if (kCount < actual.size()) {
- return ("Differ at #" + IntToString(kCount) +
+ return ("Differ at #" + std::to_string(kCount) +
": expected at EOF, actual=" + actual[kCount]);
} else if (actual.size() < kCount) {
- return ("Differ at #" + IntToString(kCount) + ": expected=" +
+ return ("Differ at #" + std::to_string(kCount) + ": expected=" +
expected[actual.size()] + ", actual at EOF");
} else {
return "";
diff --git a/run_iwyu_tests.py b/run_iwyu_tests.py
index 057ffa0..a566949 100755
--- a/run_iwyu_tests.py
+++ b/run_iwyu_tests.py
@@ -113,6 +113,8 @@ class OneIwyuTest(unittest.TestCase):
self.Include('macro_defined_by_includer-prefix.h')],
'macro_location.cc': ['-Wno-sizeof-pointer-div'],
'ms_inline_asm.cc': ['-fms-extensions'],
+ 'operator_new.cc': ['-std=c++17'],
+ 'placement_new.cc': ['-std=c++17'],
'prefix_header_attribution.cc': [self.Include('prefix_header_attribution-d1.h')],
'prefix_header_includes_add.cc': prefix_headers,
'prefix_header_includes_keep.cc': prefix_headers,
@@ -180,9 +182,11 @@ class OneIwyuTest(unittest.TestCase):
'no_fwd_decls.cc': ['.'],
'no_h_includes_cc.cc': ['.'],
'non_transitive_include.cc': ['.'],
+ 'operator_new.cc': ['.'],
'overloaded_class.cc': ['.'],
'pch_in_code.cc': ['.'],
'pointer_arith.cc': ['.'],
+ 'placement_new.cc': ['.'],
'pragma_associated.cc': ['.'],
'precomputed_tpl_args.cc': ['.'],
'prefix_header_attribution.cc': ['.'],
@@ -196,6 +200,7 @@ class OneIwyuTest(unittest.TestCase):
'relative_exported_mapped_include.cc': ['tests/cxx/subdir'],
'remove_fwd_decl_when_including.cc': ['.'],
'self_include.cc': ['.'],
+ 'sizeof_in_template_arg.cc': ['.'],
'sizeof_reference.cc': ['.'],
'specialization_needs_decl.cc': ['.'],
'system_namespaces.cc': ['.'],
diff --git a/tests/cxx/alias_template.cc b/tests/cxx/alias_template.cc
index e25032b..1c9f896 100644
--- a/tests/cxx/alias_template.cc
+++ b/tests/cxx/alias_template.cc
@@ -11,19 +11,19 @@
#include "tests/cxx/direct.h"
-template<class T> struct FullUseTemplateArg {
+template<class T> struct FullUseTemplateArgInSizeof {
char argument[sizeof(T)];
};
// Test that we go through alias template and handle aliased template
// specialization.
-template<class T> using Alias = FullUseTemplateArg<T>;
+template<class T> using Alias = FullUseTemplateArgInSizeof<T>;
// IWYU: IndirectClass needs a declaration
// IWYU: IndirectClass is...*indirect.h
Alias<IndirectClass> alias;
// Test following through entire chain of aliases.
-template<class T> using AliasChain1 = FullUseTemplateArg<T>;
+template<class T> using AliasChain1 = FullUseTemplateArgInSizeof<T>;
template<class T> using AliasChain2 = AliasChain1<T>;
// IWYU: IndirectClass needs a declaration
// IWYU: IndirectClass is...*indirect.h
@@ -33,6 +33,25 @@ AliasChain2<IndirectClass> aliasChain;
template<class T> using Pointer = T*;
Pointer<int> intPtr;
+template <class T>
+struct FullUseTemplateArgAsVar {
+ T t;
+};
+
+// Test the used class being nested deeper in the alias
+template <typename T>
+using AliasNested = FullUseTemplateArgAsVar<FullUseTemplateArgAsVar<T>>;
+
+// IWYU: IndirectClass needs a declaration
+// IWYU: IndirectClass is...*indirect.h
+AliasNested<IndirectClass> aliasNested;
+
+template <typename T>
+using AliasNested2 = FullUseTemplateArgInSizeof<FullUseTemplateArgInSizeof<T>>;
+// IWYU: IndirectClass needs a declaration
+// IWYU: IndirectClass is...*indirect.h
+AliasNested2<IndirectClass> aliasNested2;
+
/**** IWYU_SUMMARY
tests/cxx/alias_template.cc should add these lines:
diff --git a/tests/cxx/alias_template_use-d1.h b/tests/cxx/alias_template_use-d1.h
new file mode 100644
index 0000000..7c5f333
--- /dev/null
+++ b/tests/cxx/alias_template_use-d1.h
@@ -0,0 +1,10 @@
+//===--- alias_template_use-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 "alias_template_use-i1.h"
diff --git a/tests/cxx/alias_template_use-i1.h b/tests/cxx/alias_template_use-i1.h
new file mode 100644
index 0000000..bf28969
--- /dev/null
+++ b/tests/cxx/alias_template_use-i1.h
@@ -0,0 +1,14 @@
+//===--- alias_template_use-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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "alias_template_use-i2.h"
+
+template<typename T>
+using AliasTemplate = AliasedTemplate<T>;
+
diff --git a/tests/cxx/alias_template_use-i2.h b/tests/cxx/alias_template_use-i2.h
new file mode 100644
index 0000000..fee40b8
--- /dev/null
+++ b/tests/cxx/alias_template_use-i2.h
@@ -0,0 +1,11 @@
+//===--- alias_template_use-i2.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<typename T>
+class AliasedTemplate {};
diff --git a/tests/cxx/alias_template_use.cc b/tests/cxx/alias_template_use.cc
new file mode 100644
index 0000000..51c38bb
--- /dev/null
+++ b/tests/cxx/alias_template_use.cc
@@ -0,0 +1,35 @@
+//===--- alias_template_use.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.
+//
+//===----------------------------------------------------------------------===//
+
+// Tests that use of template aliases is assigned to the header defining the
+// alias, rather than the underlying type.
+
+#include "alias_template_use-d1.h"
+
+template<typename T>
+class A {
+};
+
+class B {
+ // IWYU: AliasTemplate is...*alias_template_use-i1.h
+ A<AliasTemplate<int>> a;
+};
+
+/**** IWYU_SUMMARY
+
+tests/cxx/alias_template_use.cc should add these lines:
+#include "alias_template_use-i1.h"
+
+tests/cxx/alias_template_use.cc should remove these lines:
+- #include "alias_template_use-d1.h" // lines XX-XX
+
+The full include-list for tests/cxx/alias_template_use.cc:
+#include "alias_template_use-i1.h" // for AliasTemplate
+
+***** IWYU_SUMMARY */
diff --git a/tests/cxx/badinc.cc b/tests/cxx/badinc.cc
index 819e5c3..8c62bc0 100644
--- a/tests/cxx/badinc.cc
+++ b/tests/cxx/badinc.cc
@@ -388,19 +388,6 @@ template<typename T> void CallOverloadWithUsingShadowDecl(T t) {
I1_NamespaceTemplateFn(t);
}
-template<typename T> void CallPlacementNew(T t) {
- static char buffer[sizeof(t)];
- // These should all be iwyu violations here, even though we can't be
- // *sure* some of these are actually placment-new until we get a
- // specific type for T (at template-instantiation time).
- // IWYU: operator new is...*<new>
- new (&t) int;
- // IWYU: operator new is...*<new>
- new (buffer) T();
- // IWYU: operator new is...*<new>
- new (&t) T();
-}
-
// This is defining a class declared in badinc-i1.h, but I think it's
// correct that it's not an IWYU violation to leave out badinc-i1.h.
class I1_DefinedInCc_Class {
@@ -1178,8 +1165,6 @@ int main() {
CallOverloadedFunctionDifferentFiles(5.0f);
// This should not be an IWYU violation either: the iwyu use is in the fn.
CallOverloadWithUsingShadowDecl(5);
- // IWYU: I1_Class is...*badinc-i1.h
- CallPlacementNew(i1_class);
// Calling operator<< when the first argument is a macro. We should
// still detect that operator<< is being used here, and not in the
@@ -1554,28 +1539,16 @@ int main() {
std::list<int>* list_ptr;
list_ptr = 0;
- // Make sure we only report an iwyu for <new> because of placement-new.
- // We also need to check the argument to new.
- int* newed_int = new int;
- // IWYU: operator new is...*<new>
- new(newed_int) int(4);
// IWYU: std::vector is...*<vector>
// IWYU: I2_Enum is...*badinc-i2.h
std::vector<I2_Enum>* newed_vector
// IWYU: std::vector is...*<vector>
// IWYU: I2_Enum is...*badinc-i2.h
= new std::vector<I2_Enum>;
- // IWYU: i1_i1_classptr is...*badinc-i1.h
- // IWYU: I1_Class is...*badinc-i1.h
- // IWYU: kI1ConstInt is...*badinc-i1.h
- // IWYU: operator new is...*<new>
- new (i1_i1_classptr) I1_Class(kI1ConstInt);
// IWYU: I1_Class needs a declaration
// IWYU: I1_Class is...*badinc-i1.h
// IWYU: kI1ConstInt is...*badinc-i1.h
I1_Class* newed_i1_class_array = new I1_Class[kI1ConstInt];
- delete newed_int;
- delete (((newed_int)));
// TODO(csilvers): IWYU: I2_Enum is...*badinc-i2.h
// IWYU: std::vector is...*<vector>
delete newed_vector;
@@ -1620,24 +1593,6 @@ int main() {
// IWYU: I2_Class needs a declaration
// IWYU: I2_Class is...*badinc-i2.h
= new I1_TemplateClass<I2_Class, I1_Struct>(i1_union);
- // IWYU: I1_TemplateClass is...*badinc-i1.h
- // IWYU: I2_Class needs a declaration
- // IWYU: I2_Class is...*badinc-i2.h
- // IWYU: I1_Struct needs a declaration
- char i1_templateclass_storage[sizeof(I1_TemplateClass<I2_Class, I1_Struct>)];
- // We need full type info for i1_templateclass because we never
- // fwd-declare a class with default template parameters.
- // IWYU: I1_TemplateClass is...*badinc-i1.h
- // IWYU: I2_Class needs a declaration
- // IWYU: I1_Struct needs a declaration
- I1_TemplateClass<I2_Class, I1_Struct>* placement_newed_i1_template_class
- // IWYU: I1_Struct needs a declaration
- // IWYU: I1_Struct is...*badinc-i1.h
- // IWYU: I1_TemplateClass is...*badinc-i1.h
- // IWYU: I2_Class needs a declaration
- // IWYU: I2_Class is...*badinc-i2.h
- // IWYU: operator new is...*<new>
- = new(i1_templateclass_storage) I1_TemplateClass<I2_Class, I1_Struct>();
// IWYU: I1_Class needs a declaration
// IWYU: I1_Class is...*badinc-i1.h
// IWYU: i1_ns::I1_NamespaceClass is...*badinc-i1.h
@@ -1658,11 +1613,6 @@ int main() {
// IWYU: I1_Struct is...*badinc-i1.h
// IWYU: I1_TemplateClass is...*badinc-i1.h
delete newed_i1_template_class_ctor;
- // Make sure we handle it right when we explicitly call the dtor, as well.
- // IWYU: I2_Class::~I2_Class is...*badinc-i2-inl.h
- // IWYU: I1_TemplateClass is...*badinc-i1.h
- // IWYU: I1_Struct is...*badinc-i1.h
- placement_newed_i1_template_class->~I1_TemplateClass();
// IWYU: I1_Class is...*badinc-i1.h
delete i1_class_tpl_ctor;
// Check that we discover constructor/destructor locations as well.
@@ -1910,7 +1860,6 @@ tests/cxx/badinc.cc should add these lines:
#include <stdarg.h>
#include <stddef.h>
#include <list>
-#include <new>
#include "tests/cxx/badinc-i1.h"
class D2_Class;
class D2_ForwardDeclareClass;
@@ -1943,12 +1892,11 @@ The full include-list for tests/cxx/badinc.cc:
#include <algorithm> // for find
#include <fstream> // for fstream
#include <list> // for list
-#include <new> // for operator new
#include <string> // for basic_string, basic_string<>::iterator, operator+, string
#include <typeinfo> // for type_info
#include "tests/cxx/badinc-d1.h" // for D11, D1CopyClassFn, D1Function, D1_Class, D1_CopyClass, D1_Enum, D1_I1_Typedef, D1_StructPtr, D1_Subclass, D1_TemplateClass, D1_TemplateStructWithDefaultParam, MACRO_CALLING_I4_FUNCTION
#include "tests/cxx/badinc-d4.h" // for D4_ClassForOperator, operator<<
-#include "tests/cxx/badinc-i1.h" // for EmptyDestructorClass, H_Class::H_Class_DefinedInI1, I11, I12, I13, I1_And_I2_OverloadedFunction, I1_Base, I1_Class, I1_Class::NestedStruct, I1_ClassPtr, I1_Enum, I1_Function, I1_FunctionPtr, I1_I2_Class_Typedef, I1_MACRO_LOGGING_CLASS, I1_MACRO_SYMBOL_WITHOUT_VALUE, I1_MACRO_SYMBOL_WITH_VALUE, I1_MACRO_SYMBOL_WITH_VALUE0, I1_MACRO_SYMBOL_WITH_VALUE2, I1_ManyPtrStruct (ptr only), I1_MemberPtr, I1_NamespaceClass, I1_NamespaceStruct, I1_NamespaceTemplateFn, I1_OverloadedFunction, I1_PtrAndUseOnSameLine, I1_PtrDereferenceClass, I1_PtrDereferenceStatic, I1_PtrDereferenceStruct, I1_SiblingClass, I1_StaticMethod, I1_Struct, I1_Subclass, I1_SubclassesI2Class, I1_TemplateClass, I1_TemplateClass<>::I1_TemplateClass_int, I1_TemplateClassFwdDeclaredInD2 (ptr only), I1_TemplateFunction, I1_TemplateMethodOnlyClass, I1_TemplateSubclass, I1_Typedef, I1_TypedefOnly_Class, I1_TypedefOnly_Class<>::i, I1_Union, I1_UnnamedStruct, I1_UnusedNamespaceStruct (ptr only), I1_const_ptr, I2_OperatorDefinedInI1Class::operator<<, MACRO_CALLING_I6_FUNCTION, OperateOn, i1_GlobalFunction, i1_i1_classptr, i1_int, i1_int_global, i1_int_global2, i1_int_global2sub, i1_int_global3, i1_int_global3sub, i1_int_global4, i1_int_global4sub, i1_int_globalsub, i1_ns2, i1_ns4, i1_ns5, kI1ConstInt, operator==
+#include "tests/cxx/badinc-i1.h" // for EmptyDestructorClass, H_Class::H_Class_DefinedInI1, I11, I12, I13, I1_And_I2_OverloadedFunction, I1_Base, I1_Class, I1_Class::NestedStruct, I1_ClassPtr, I1_Enum, I1_Function, I1_FunctionPtr, I1_I2_Class_Typedef, I1_MACRO_LOGGING_CLASS, I1_MACRO_SYMBOL_WITHOUT_VALUE, I1_MACRO_SYMBOL_WITH_VALUE, I1_MACRO_SYMBOL_WITH_VALUE0, I1_MACRO_SYMBOL_WITH_VALUE2, I1_ManyPtrStruct (ptr only), I1_MemberPtr, I1_NamespaceClass, I1_NamespaceStruct, I1_NamespaceTemplateFn, I1_OverloadedFunction, I1_PtrAndUseOnSameLine, I1_PtrDereferenceClass, I1_PtrDereferenceStatic, I1_PtrDereferenceStruct, I1_SiblingClass, I1_StaticMethod, I1_Struct, I1_Subclass, I1_SubclassesI2Class, I1_TemplateClass, I1_TemplateClass<>::I1_TemplateClass_int, I1_TemplateClassFwdDeclaredInD2 (ptr only), I1_TemplateFunction, I1_TemplateMethodOnlyClass, I1_TemplateSubclass, I1_Typedef, I1_TypedefOnly_Class, I1_TypedefOnly_Class<>::i, I1_Union, I1_UnnamedStruct, I1_UnusedNamespaceStruct (ptr only), I1_const_ptr, I2_OperatorDefinedInI1Class::operator<<, MACRO_CALLING_I6_FUNCTION, OperateOn, i1_GlobalFunction, i1_int, i1_int_global, i1_int_global2, i1_int_global2sub, i1_int_global3, i1_int_global3sub, i1_int_global4, i1_int_global4sub, i1_int_globalsub, i1_ns2, i1_ns4, i1_ns5, kI1ConstInt, operator==
#include "tests/cxx/badinc2.c"
class D2_Class;
class D2_ForwardDeclareClass;
diff --git a/tests/cxx/builtins_new_included.cc b/tests/cxx/builtins_new_included.cc
deleted file mode 100644
index 76efc7b..0000000
--- a/tests/cxx/builtins_new_included.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-//===--- builtins_new_included.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.
-//
-//===----------------------------------------------------------------------===//
-
-// Test that iwyu suggests the include for <new> be removed if only
-// built-in functions are used.
-
-#include <new>
-
-void foo() {
- char* ch = new char;
- delete ch;
- int* int_array = new int[10];
- delete[] int_array;
-}
-
-/**** IWYU_SUMMARY
-
-tests/cxx/builtins_new_included.cc should add these lines:
-
-tests/cxx/builtins_new_included.cc should remove these lines:
-- #include <new> // lines XX-XX
-
-The full include-list for tests/cxx/builtins_new_included.cc:
-
-***** IWYU_SUMMARY */
diff --git a/tests/cxx/operator_new.cc b/tests/cxx/operator_new.cc
new file mode 100644
index 0000000..0019bf3
--- /dev/null
+++ b/tests/cxx/operator_new.cc
@@ -0,0 +1,86 @@
+//===--- operator_new.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.
+//
+//===----------------------------------------------------------------------===//
+
+// Test that iwyu suggests the include for <new> be removed if only
+// built-in functions are used.
+
+#include <new>
+#include "tests/cxx/direct.h"
+
+// The most primitive ::operator new/delete are builtins, and are basically
+// wrappers around malloc.
+void ExplicitOperators() {
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ IndirectClass* elem = (IndirectClass*)::operator new(sizeof(IndirectClass));
+ ::operator delete(elem);
+
+ // IWYU: IndirectClass needs a declaration
+ IndirectClass* arr =
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ (IndirectClass*)::operator new[](4 * sizeof(IndirectClass));
+ ::operator delete[](arr);
+}
+
+// New- and delete-expressions, unless using placement syntax, only use builtin
+// operators. They're equivalent with the above, but also run ctors/dtors.
+// For placement syntax, see tests/cxx/placement_new.cc
+void ExpressionsBuiltinTypes() {
+ char* elem = new char;
+ delete elem;
+
+ int* arr = new int[4];
+ delete[] arr;
+}
+
+// New- and delete-expressions with user-defined types.
+void ExpressionsUserTypes() {
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ IndirectClass* elem = new IndirectClass;
+ // IWYU: IndirectClass is...*indirect.h
+ delete elem;
+
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ IndirectClass* arr = new IndirectClass[4];
+ // IWYU: IndirectClass is...*indirect.h
+ delete[] arr;
+}
+
+// Aligned allocation uses operator new(size_t, std::align_val_t) under the
+// hood in C++17, but does not require <new> to be included for it. Pre-C++17,
+// the alignment is silently ignored (or unsupported if the standard library
+// does not support aligned allocation).
+void ImplicitAlignedAllocation() {
+ struct alignas(32) Aligned {
+ float value[8];
+ };
+
+ Aligned* elem = new Aligned;
+ delete elem;
+
+ Aligned* arr = new Aligned[10];
+ delete[] arr;
+}
+
+/**** IWYU_SUMMARY
+
+tests/cxx/operator_new.cc should add these lines:
+#include "tests/cxx/indirect.h"
+
+tests/cxx/operator_new.cc should remove these lines:
+- #include <new> // lines XX-XX
+- #include "tests/cxx/direct.h" // lines XX-XX
+
+The full include-list for tests/cxx/operator_new.cc:
+#include "tests/cxx/indirect.h" // for IndirectClass
+
+***** IWYU_SUMMARY */
diff --git a/tests/cxx/placement_new-d1.h b/tests/cxx/placement_new-d1.h
new file mode 100644
index 0000000..03e4d54
--- /dev/null
+++ b/tests/cxx/placement_new-d1.h
@@ -0,0 +1,15 @@
+//===--- placement_new-d1.h - test input file for iwyu ---*- C++ -*--------===//
+//
+// 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_CXX_PLACEMENT_NEW_D1_H_
+#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_PLACEMENT_NEW_D1_H_
+
+#include "tests/cxx/placement_new-i1.h"
+
+#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_PLACEMENT_NEW_D1_H_
diff --git a/tests/cxx/placement_new-i1.h b/tests/cxx/placement_new-i1.h
new file mode 100644
index 0000000..3f5e4fb
--- /dev/null
+++ b/tests/cxx/placement_new-i1.h
@@ -0,0 +1,24 @@
+//===--- placement_new-i1.h - test input file for iwyu ----*- C++ -*-------===//
+//
+// 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_CXX_PLACEMENT_NEW_I1_H_
+#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_PLACEMENT_NEW_I1_H_
+
+#include <new>
+
+template <class T, class U>
+class ClassTemplate {
+ public:
+ ClassTemplate() = default;
+
+ T first;
+ U second;
+};
+
+#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_PLACEMENT_NEW_I1_H_
diff --git a/tests/cxx/placement_new.cc b/tests/cxx/placement_new.cc
new file mode 100644
index 0000000..a879a4a
--- /dev/null
+++ b/tests/cxx/placement_new.cc
@@ -0,0 +1,151 @@
+//===--- placement_new.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.
+//
+//===----------------------------------------------------------------------===//
+
+// Test that use of placement-new requires include of <new> in all the usual
+// scenarios.
+//
+// Requires -std=c++17 on the command-line to have std::aligned_val_t available
+// in <new>.
+
+#include "tests/cxx/direct.h"
+#include "tests/cxx/placement_new-d1.h"
+
+// Placement new of builtin types.
+void PlacementNewBuiltinType() {
+ // Make sure we only report a use of <new> because of placement new, not
+ // ordinary new-expressions.
+ int* newed_int = new int;
+ // IWYU: operator new is...*<new>
+ new (newed_int) int(4);
+
+ delete newed_int;
+}
+
+// Placement new of user-defined type.
+void PlacementNewUserType() {
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ IndirectClass* icptr = new IndirectClass;
+
+ // IWYU: IndirectClass is...*indirect.h
+ // IWYU: operator new is...*<new>
+ new (icptr) IndirectClass;
+
+ // IWYU: IndirectClass is...*indirect.h
+ delete icptr;
+}
+
+// Placement new in macro, use is attributed to the macro.
+static char global_buffer[256];
+// IWYU: operator new is...*<new>
+#define CONSTRUCT_GLOBAL(T) new (global_buffer) T;
+
+void PlacementNewInMacro() {
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ IndirectClass* a = CONSTRUCT_GLOBAL(IndirectClass);
+}
+
+// Placement new inside a template.
+template <typename T>
+void PlacementNewInTemplate(T t) {
+ static char buffer[sizeof(t)];
+ // These should all be iwyu violations here, even though we can't be
+ // *sure* some of these are actually placement new until we get a
+ // specific type for T (at template-instantiation time).
+ // IWYU: operator new is...*<new>
+ new (&t) int;
+ // IWYU: operator new is...*<new>
+ new (buffer) T();
+ // IWYU: operator new is...*<new>
+ new (&t) T();
+}
+
+// Placement new when the newed type _is_ a template.
+void PlacementNewOfTemplate() {
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ // IWYU: ClassTemplate is...*placement_new-i1.h
+ char template_storage[sizeof(ClassTemplate<IndirectClass, IndirectClass>)];
+
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: ClassTemplate needs a declaration
+ ClassTemplate<IndirectClass, IndirectClass>* placement_newed_template =
+ // Need <new> because of placement new, and requires both template and
+ // arguments as complete types.
+ //
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ // IWYU: ClassTemplate is...*placement_new-i1.h
+ // IWYU: operator new is...*<new>
+ new (template_storage) ClassTemplate<IndirectClass, IndirectClass>();
+
+ // Make sure we handle it right when we explicitly call the dtor, as well.
+ // IWYU: ClassTemplate is...*placement_new-i1.h
+ placement_newed_template->~ClassTemplate();
+}
+
+// new(std::nothrow) is not strictly placement allocation, but it uses placement
+// syntax to adjust exception policy.
+// To use 'std::nothrow' we must include <new>, even if we don't need it for
+// 'new' itself.
+void NoThrow() {
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ // IWYU: std::nothrow is...*<new>
+ IndirectClass* elem = new (std::nothrow) IndirectClass;
+ // IWYU: IndirectClass is...*indirect.h
+ delete elem;
+
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ // IWYU: std::nothrow is...*<new>
+ IndirectClass* arr = new (std::nothrow) IndirectClass[4];
+ // IWYU: IndirectClass is...*indirect.h
+ delete[] arr;
+}
+
+// new(std::align_val_t) is not strictly placement allocation, but it uses
+// placement syntax to provide alignment hints.
+// To use 'std::align_val_t' we must include <new>, even if we don't need it
+// for 'new' itself.
+// The aligned allocation mechanics are only available as of C++17.
+void ExplicitAlignedAllocation() {
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ // IWYU: std::align_val_t is...*<new>
+ IndirectClass* elem = new (std::align_val_t(32)) IndirectClass;
+ // IWYU: IndirectClass is...*indirect.h
+ delete elem;
+
+ // IWYU: IndirectClass needs a declaration
+ // IWYU: IndirectClass is...*indirect.h
+ // IWYU: std::align_val_t is...*<new>
+ IndirectClass* arr = new (std::align_val_t(32)) IndirectClass[10];
+ // IWYU: IndirectClass is...*indirect.h
+ delete[] arr;
+}
+
+/**** IWYU_SUMMARY
+
+tests/cxx/placement_new.cc should add these lines:
+#include <new>
+#include "tests/cxx/indirect.h"
+#include "tests/cxx/placement_new-i1.h"
+
+tests/cxx/placement_new.cc should remove these lines:
+- #include "tests/cxx/direct.h" // lines XX-XX
+- #include "tests/cxx/placement_new-d1.h" // lines XX-XX
+
+The full include-list for tests/cxx/placement_new.cc:
+#include <new> // for align_val_t, nothrow, operator new
+#include "tests/cxx/indirect.h" // for IndirectClass
+#include "tests/cxx/placement_new-i1.h" // for ClassTemplate
+
+***** IWYU_SUMMARY */
diff --git a/tests/cxx/precomputed_tpl_args.cc b/tests/cxx/precomputed_tpl_args.cc
index a98a09e..cfb9e0a 100644
--- a/tests/cxx/precomputed_tpl_args.cc
+++ b/tests/cxx/precomputed_tpl_args.cc
@@ -61,11 +61,10 @@ std::bitset<5> bitset;
// for map<T, SpecializationClass>, we should only consider T.
template<typename T> class TemplatedClass {
- // TODO(csilvers): IWYU: SpecializationClass is...*precomputed_tpl_args-i1.h
- // TODO(csilvers): IWYU: std::less is...*precomputed_tpl_args-i1.h
+ // IWYU: SpecializationClass is...*precomputed_tpl_args-i1.h
// IWYU: SpecializationClass needs a declaration
std::map<SpecializationClass, T> t1;
- // TODO(csilvers): IWYU: IndirectClass is...*precomputed_tpl_args-i1.h
+ // IWYU: IndirectClass is...*precomputed_tpl_args-i1.h
// IWYU: IndirectClass needs a declaration
std::map<T, IndirectClass> t3;
};
diff --git a/tests/cxx/sizeof_in_template_arg.cc b/tests/cxx/sizeof_in_template_arg.cc
new file mode 100644
index 0000000..1f555d8
--- /dev/null
+++ b/tests/cxx/sizeof_in_template_arg.cc
@@ -0,0 +1,41 @@
+//===--- sizeof_in_template_arg.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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tests/cxx/direct.h"
+
+// This verifies that using sizeof(...) means that the argument of sizeof
+// doesn't count as being in a forward-declare context. In particular, when
+// it's used as a template argument.
+
+template <unsigned long Size>
+struct Storage {
+ char storage[Size];
+};
+
+template <typename T>
+struct Public {
+ Storage<sizeof(T)> storage;
+};
+
+// IWYU: IndirectClass is...*indirect.h
+// IWYU: IndirectClass needs a declaration
+Public<IndirectClass> p;
+
+/**** IWYU_SUMMARY
+
+tests/cxx/sizeof_in_template_arg.cc should add these lines:
+#include "tests/cxx/indirect.h"
+
+tests/cxx/sizeof_in_template_arg.cc should remove these lines:
+- #include "tests/cxx/direct.h" // lines XX-XX
+
+The full include-list for tests/cxx/sizeof_in_template_arg.cc:
+#include "tests/cxx/indirect.h" // for IndirectClass
+
+***** IWYU_SUMMARY */
diff --git a/tests/cxx/specialization_needs_decl-d1.h b/tests/cxx/specialization_needs_decl-d1.h
index 2b0151d..9d5eff3 100644
--- a/tests/cxx/specialization_needs_decl-d1.h
+++ b/tests/cxx/specialization_needs_decl-d1.h
@@ -7,6 +7,8 @@
//
//===----------------------------------------------------------------------===//
+#include "tests/cxx/specialization_needs_decl-i1.h"
+
template <typename T> struct TplStruct { };
template <> struct TplStruct<float> { };
diff --git a/tests/cxx/specialization_needs_decl-i1.h b/tests/cxx/specialization_needs_decl-i1.h
new file mode 100644
index 0000000..42e8778
--- /dev/null
+++ b/tests/cxx/specialization_needs_decl-i1.h
@@ -0,0 +1,18 @@
+//===--- specialization_needs_decl-i1.h - test input file -------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// This is employed to show that when the template specialization is used, the
+// base template is not required in full. Issue #735.
+
+// Base template
+template<typename>
+struct Template;
+
+// Specialization for int
+template<> struct Template<int> { int x; };
diff --git a/tests/cxx/specialization_needs_decl.cc b/tests/cxx/specialization_needs_decl.cc
index 91b3e61..750cbdc 100644
--- a/tests/cxx/specialization_needs_decl.cc
+++ b/tests/cxx/specialization_needs_decl.cc
@@ -19,15 +19,26 @@ template<> struct TplStruct<int> { };
// the definition.
template<> struct TplStruct<float>;
+// Full-using a specialization requires definition of the specialization to be
+// included. Not the base template.
+
+// IWYU: Template needs a declaration
+int f(Template<int>& t) {
+ // IWYU: Template is...*specialization_needs_decl-i1.h
+ return t.x;
+}
+
/**** IWYU_SUMMARY
tests/cxx/specialization_needs_decl.cc should add these lines:
+#include "tests/cxx/specialization_needs_decl-i1.h"
template <typename T> struct TplStruct;
tests/cxx/specialization_needs_decl.cc should remove these lines:
- #include "tests/cxx/specialization_needs_decl-d1.h" // lines XX-XX
The full include-list for tests/cxx/specialization_needs_decl.cc:
+#include "tests/cxx/specialization_needs_decl-i1.h" // for Template
template <typename T> struct TplStruct;
***** IWYU_SUMMARY */
diff --git a/tests/cxx/template_specialization.cc b/tests/cxx/template_specialization.cc
index 0e37fa9..12fa374 100644
--- a/tests/cxx/template_specialization.cc
+++ b/tests/cxx/template_specialization.cc
@@ -11,6 +11,7 @@
// it to the right location.
#include "tests/cxx/template_specialization-d1.h"
+#include "tests/cxx/direct.h"
template<typename T> class Foo;
@@ -38,18 +39,30 @@ TplTplStruct<> tts;
TplTplStruct<Foo> tts2;
+template<typename T>
+struct Specialized;
+
+template<>
+// IWYU: IndirectClass is...*indirect.h
+struct Specialized<int> : IndirectClass {};
+
+
/**** IWYU_SUMMARY
tests/cxx/template_specialization.cc should add these lines:
+#include "tests/cxx/indirect.h"
#include "tests/cxx/template_specialization-i1.h"
#include "tests/cxx/template_specialization-i2.h"
tests/cxx/template_specialization.cc should remove these lines:
+- #include "tests/cxx/direct.h" // lines XX-XX
- #include "tests/cxx/template_specialization-d1.h" // lines XX-XX
- template <typename T> class Foo; // lines XX-XX
The full include-list for tests/cxx/template_specialization.cc:
+#include "tests/cxx/indirect.h" // for IndirectClass
#include "tests/cxx/template_specialization-i1.h" // for Foo
#include "tests/cxx/template_specialization-i2.h" // for Foo
+template <typename T> struct Specialized; // lines XX-XX+1
***** IWYU_SUMMARY */
diff --git a/tests/cxx/typedef_in_template.cc b/tests/cxx/typedef_in_template.cc
index 48721d7..8e27810 100644
--- a/tests/cxx/typedef_in_template.cc
+++ b/tests/cxx/typedef_in_template.cc
@@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//
+#include "tests/cxx/direct.h"
#include "tests/cxx/typedef_in_template-d1.h"
template<class T>
@@ -24,34 +25,87 @@ class Container {
void Declarations() {
- // These do not need the full type for Class because they're template params.
+ // Just using Container does not need the full type for Class because there
+ // are only aliases made, which do not require full-uses.
- // TODO: This is almost certainly wrong, see bug #431
- // We should not require the full definition of Class for passing it as a
- // template argument, but we must require it when the typedef it's aliasing
- // is full-used.
- // The bug has instructions for how to provoke the error more obviously.
+ // TODO: But currently this is counted as a full-use because Class is used
+ // inside a template specialization (of Pair) within the definition of
+ // Container.
+ // IWYU: Class is...*typedef_in_template-i1.h
+ // IWYU: Class needs a declaration
+ Container<Class> c;
+
+ // Full-using any of those aliases *should* require a full use of Class.
+ // IWYU: Class is...*typedef_in_template-i1.h
// IWYU: Class needs a declaration
Container<Class>::value_type vt;
+ // IWYU: Class is...*typedef_in_template-i1.h
// IWYU: Class needs a declaration
Container<Class>::pair_type pt;
+ // IWYU: Class is...*typedef_in_template-i1.h
// IWYU: Class needs a declaration
Container<Class>::alias_type at;
}
+// STL containers are often implemented via a complex web of type aliases and
+// helper classes. Tracking uses through all these layers can be non-trivial.
+// The following are some reduced examples in roughly increasing order of
+// complexity which can serve as helpful test cases while debugging such
+// issues. They were inspired by libstdc++'s implementation of
+// std::unordered_map, but don't directly correspond to it.
+
+// Verify that a full-use of an alias of a template parameter is treated as a
+// full-use of that parameter.
+template <typename T>
+struct UsesAliasedParameter {
+ using TAlias = T;
+ TAlias t;
+};
+
+// IWYU: IndirectClass is...*indirect.h
+// IWYU: IndirectClass needs a declaration
+UsesAliasedParameter<IndirectClass> a;
+
+// IWYU: IndirectClass is...*indirect.h
+// IWYU: IndirectClass needs a declaration
+UsesAliasedParameter<IndirectClass>::TAlias a2;
+
+// Try a more complex example, through an additional layer of indirection.
+template <typename T>
+struct IndirectlyUsesAliasedParameter {
+ using TAlias = typename UsesAliasedParameter<T>::TAlias;
+ TAlias t;
+};
+
+// IWYU: IndirectClass is...*indirect.h
+// IWYU: IndirectClass needs a declaration
+IndirectlyUsesAliasedParameter<IndirectClass> b;
+
+template <typename T>
+struct NestedUseOfAliasedParameter {
+ using UserAlias = UsesAliasedParameter<T>;
+ UserAlias a;
+};
+
+// IWYU: IndirectClass is...*indirect.h
+// IWYU: IndirectClass needs a declaration
+NestedUseOfAliasedParameter<IndirectClass> c;
/**** IWYU_SUMMARY
tests/cxx/typedef_in_template.cc should add these lines:
+#include "tests/cxx/indirect.h"
#include "tests/cxx/typedef_in_template-i1.h"
tests/cxx/typedef_in_template.cc should remove these lines:
+- #include "tests/cxx/direct.h" // lines XX-XX
- #include "tests/cxx/typedef_in_template-d1.h" // lines XX-XX
The full include-list for tests/cxx/typedef_in_template.cc:
-#include "tests/cxx/typedef_in_template-i1.h" // for Class (ptr only), Pair
+#include "tests/cxx/indirect.h" // for IndirectClass
+#include "tests/cxx/typedef_in_template-i1.h" // for Class, Pair
***** IWYU_SUMMARY */