summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Venegas <cvenegas@esri.com>2018-07-11 16:20:20 -0700
committerKim Grasman <kim.grasman@gmail.com>2018-08-18 11:56:27 +0200
commitb290cf5915302f85e0cbf606e09eb1bc66bf6e18 (patch)
tree6e1449e1f05f4013b6c5f587ece5115162a40bb5
parent3e110237e71e523a27b78911e4b6337377203135 (diff)
[fix_includes] Enhance to understand multi-level namespaces
The fix_includes.py script has logic to append new forward declares at the namespace level if possible. This logic only tries on the first namespace encountered, however, so projects with a lot of namespaces require a lot of manual tidying up. Add multi-level detection so nested namespaces have includes automatically inserted without creating new namespace blocks when it is possible to do so cleanly. If any issues are encountered when evaluating namespaces, new forward declares will be added at the top level. Ifdeffed code will also be ignored when trying to evaluate namespaces. Signed-off-by: Christian Venegas <cvenegas@esri.com>
-rwxr-xr-xfix_includes.py239
-rwxr-xr-xfix_includes_test.py300
2 files changed, 441 insertions, 98 deletions
diff --git a/fix_includes.py b/fix_includes.py
index 0bce5bb..5bd42a7 100755
--- a/fix_includes.py
+++ b/fix_includes.py
@@ -1324,12 +1324,12 @@ def _GetToplevelReorderSpans(file_lines):
return good_reorder_spans
-def _GetFirstNamespaceLevelReorderSpan(file_lines):
- """Returns the first reorder-span inside a namespace, if it's easy to do.
+def _GetNamespaceLevelReorderSpans(file_lines):
+ """Returns a list of reorder-spans inside namespaces, if it's easy to do.
This routine is meant to handle the simple case where code consists
of includes and forward-declares, and then a 'namespace
- my_namespace'. We return the reorder span of the inside-namespace
+ my_namespace'. We return the reorder spans of the inside-namespace
forward-declares, which is a good place to insert new
inside-namespace forward-declares (rather than putting these new
forward-declares at the top level).
@@ -1339,87 +1339,162 @@ def _GetFirstNamespaceLevelReorderSpan(file_lines):
it then continues until it finds a forward-declare line, or a
non-namespace contentful line. In the former case, it figures out
the reorder-span this forward-declare line is part of, while in the
- latter case it creates a new reorder-span. It returns
- (enclosing_namespaces, reorder_span).
+ latter case it creates a new reorder-span. A list of these namespace
+ reorder spans are returned so they can all be checked. These elements
+ are in the form (enclosing_namespace, reorder_span).
Arguments:
file_lines: an array of LineInfo objects with .type and
- .reorder_span filled in.
+ .reorder_span filled in.
Returns:
- (None, None) if we could not find a first namespace-level
- reorder-span, or (enclosing_namespaces, reorder_span), where
- enclosing_namespaces is a string that looks like (for instance)
+ [] if we could not find any namespace-level reorder-spans, or
+ [(enclosing_namespace, reorder_span), ...], where enclosing_namespace
+ is a string that looks like (for instance)
'namespace ns1 { namespace ns2 {', and reorder-span is a
[start_line, end_line) pair.
"""
- simple_namespace_re = re.compile(
- r'^\s*namespace\s+([^{\s]+)\s*\{\s*(//.*)?$')
- simple_allman_namespace_re = re.compile(
- r'^\s*namespace\s+([^{\s]+)\s*(//.*)?$')
- namespace_prefix = ''
- for line_number in range(len(file_lines)):
- line_info = file_lines[line_number]
+ def _GetNamespaceNames(namespace_line):
+ """Returns a list of namespace names given a namespace line. Anonymous
+ namespaces will return an empty string
+ """
+ namespace_re = re.compile(r'\s*namespace\b(.*)')
+ namespaces = []
+ namespace_line = namespace_line.split("/")[0] # remove C++ comments
+ namespace_line = namespace_line.split("{") # extract all namespaces
+ for namespace in namespace_line:
+ m = namespace_re.match(namespace)
+ if m:
+ namespaces.append(m.group(1).strip())
- if line_info.deleted:
- continue
+ return namespaces
- # If we're an empty line, just ignore us. Likewise with #include
- # lines, which aren't 'contentful' for our purposes, and the
- # header guard, which is (by definition) the only kind of #ifdef
- # that we can be inside and still considered at the "top level".
- if line_info.type in (_COMMENT_LINE_RE, _BLANK_LINE_RE, _INCLUDE_RE,
- _HEADER_GUARD_RE, _HEADER_GUARD_DEFINE_RE,
- _PRAGMA_ONCE_LINE_RE):
- continue
+ namespace_reorder_spans = {}
+ try:
+ namespace_prefixes = []
+ pending_namespace_prefix = ''
+ ifdef_depth = 0
- # If we're a 'contentful' line such as a (non-header-guard) #ifdef, bail.
- elif line_info.type in (_IF_RE, _NAMESPACE_END_RE, _ELSE_RE, _ENDIF_RE,
- None): # None is a 'normal' contentful line
- # TODO(csilvers): we could probably keep going if there are no
- # braces on the line. We could also keep track of our #ifdef
- # depth instead of bailing on #else and #endif, and only accept
- # the fwd-decl-inside-namespace if it's at ifdef-depth 0.
- break
+ for line_number, line_info in enumerate(file_lines):
+ if line_info.deleted:
+ continue
- elif line_info.type == _NAMESPACE_START_RE:
- # Only handle the simple case of 'namespace <foo> {'
- m = simple_namespace_re.match(line_info.line)
- if not m:
- break
- namespace_prefix += ('namespace %s { ' % m.group(1).strip())
+ # If we're an empty line, just ignore us. Likewise with #include
+ # lines, which aren't 'contentful' for our purposes, and the
+ # header guard, which is (by definition) the only kind of #ifdef
+ # that we can be inside and still considered at the "top level".
+ if line_info.type in (_COMMENT_LINE_RE,
+ _BLANK_LINE_RE,
+ _INCLUDE_RE,
+ _HEADER_GUARD_RE,
+ _HEADER_GUARD_DEFINE_RE,
+ _PRAGMA_ONCE_LINE_RE):
+ continue
- elif line_info.type in (_NAMESPACE_START_ALLMAN_RE,
- _NAMESPACE_START_MIXED_RE):
- # Trim any possible comments on namespace to properly build
- # the namespace level spans
- m = simple_allman_namespace_re.match(line_info.line)
- if not m:
- break
- namespace_prefix += ('namespace %s' % m.group(1).strip())
+ # If we're a 'contentful' line such as a (non-header-guard) #ifdef, add
+ # to the ifdef depth. If we encounter #endif, reduce the ifdef depth.
+ # Only keep track of namespaces when ifdef depth is 0
+ elif line_info.type == _IF_RE:
+ ifdef_depth += 1
- elif line_info.type == _NAMESPACE_CONTINUE_ALLMAN_MIXED_RE:
- # Append to the simplified Allman namespace
- namespace_prefix += ' { '
+ elif line_info.type == _ELSE_RE:
+ continue
- elif line_info.type == _FORWARD_DECLARE_RE:
- # If we're not in a namespace, keep going. Otherwise, this is
- # just the situation we're looking for!
- if namespace_prefix:
- return (namespace_prefix, line_info.reorder_span)
+ elif line_info.type == _ENDIF_RE:
+ ifdef_depth -= 1
+
+ elif ifdef_depth != 0:
+ continue # skip lines until we're outside of an ifdef block
+
+ # Build the simplified namespace dictionary. When any new namespace is
+ # encountered, add the namespace to the list using the next line to cover
+ # namespaces without forward declarations. When a forward declare is
+ # found, update the dictionary using the existing namespace span that the
+ # forward declare contains. Once a contentful line (None) has been found
+ # or any exception occurs, return the results that have been found. Any
+ # forward declare that wasn't able to have a proper namespace name found
+ # will still propagate to the top of the file.
+ elif line_info.type == _NAMESPACE_START_RE:
+ for namespace in _GetNamespaceNames(line_info.line):
+ if not namespace:
+ namespace_prefixes.append('namespace {')
+ else:
+ namespace_prefixes.append('namespace %s {' % namespace)
- else:
- # We should have handled all the cases above!
- assert False, ('unknown line-info type',
- _LINE_TYPES.index(line_info.type))
+ namespace_reorder_spans[' '.join(namespace_prefixes)] = (
+ line_number+1, line_number+1)
+
+ elif line_info.type == _NAMESPACE_START_ALLMAN_RE:
+ pending_namespace_prefix = ''
+ namespaces = _GetNamespaceNames(line_info.line)
+ if len(namespaces) != 1:
+ raise FixIncludesError('Allman namespace found containing multiple '
+ 'names: %s', line_info.line)
+ for namespace in namespaces:
+ if not namespace:
+ pending_namespace_prefix += 'namespace'
+ else:
+ pending_namespace_prefix += 'namespace %s' % namespace
+
+ elif line_info.type == _NAMESPACE_START_MIXED_RE:
+ # For mixed namespace styles, we need to append normalized prefixes
+ # using regular and Allman style. Treat the first elements as
+ # normal and only treat the final element as Allman. By the
+ # nature of mixed namespaces, there will always be more than
+ # one namespace so it is okay to assume that _GetNamespaceNames
+ # will always return multiple records.
+ pending_namespace_prefix = ''
+ namespaces = _GetNamespaceNames(line_info.line)
+ for namespace in namespaces[:-1]:
+ if not namespace:
+ namespace_prefixes.append('namespace {')
+ else:
+ namespace_prefixes.append('namespace %s {' % namespace)
+
+ if not namespaces[-1]:
+ pending_namespace_prefix += 'namespace'
+ else:
+ pending_namespace_prefix += 'namespace %s' % namespaces[-1]
+
+ elif line_info.type == _NAMESPACE_CONTINUE_ALLMAN_MIXED_RE:
+ # Append to the simplified allman namespace.
+ if pending_namespace_prefix == '':
+ raise FixIncludesError('Namespace bracket found without an associated '
+ 'namespace name at line: %s', line_number)
+ pending_namespace_prefix += ' {'
+ namespace_prefixes.append(pending_namespace_prefix)
+ namespace_reorder_spans[' '.join(namespace_prefixes)] = (
+ line_number+1, line_number+1)
+
+ elif line_info.type == _NAMESPACE_END_RE:
+ # Remove C++ comments and count the ending brackets.
+ namespace_end_count = line_info.line.split("/")[0].count("}")
+ namespace_prefixes = namespace_prefixes[:-namespace_end_count]
+
+ elif line_info.type == _FORWARD_DECLARE_RE:
+ # If we're not in a namespace, keep going. Otherwise, this is
+ # just the situation we're looking for! Update the dictionary
+ # with the better reorder span
+ if len(namespace_prefixes) > 0:
+ namespace_reorder_spans[' '.join(namespace_prefixes)] = (
+ line_info.reorder_span)
+
+ elif line_info.type == None:
+ break
+
+ else:
+ # We should have handled all the cases above!
+ assert False, ('unknown line-info type',
+ _LINE_TYPES.index(line_info.type))
+ except Exception as why:
+ # Namespace detection could be tricky so take what we have and return.
+ print('DEBUG: Namespace detection returned prematurely because of an '
+ 'exception: %s' % (why))
+ pass
- # We stopped because we hit a contentful line (or, possibly, a
- # weird-looking namespace). If we're inside the first-namespace,
- # return this position as a good place to insert forward-declares.
- if namespace_prefix:
- return (namespace_prefix, (line_number, line_number))
- return (None, None)
+ # return a reverse sorted list so longest matches are checked first
+ return sorted(namespace_reorder_spans.items(), reverse=True)
# These are potential 'kind' arguments to _FirstReorderSpanWith.
@@ -1816,19 +1891,21 @@ def _DecoratedMoveSpanLines(iwyu_record, file_lines, move_span_lines, flags):
# If we're a forward-declare inside a namespace, see if there's a
# reorder span inside the same namespace we can fit into.
if kind == _FORWARD_DECLARE_KIND:
- (namespace_prefix, possible_reorder_span) = \
- _GetFirstNamespaceLevelReorderSpan(file_lines)
- if (namespace_prefix and possible_reorder_span and
- firstline.line.startswith(namespace_prefix)):
- # Great, we can go into this reorder_span. We also need to
- # modify all-lines because this line doesn't need the
- # namespace prefix anymore. Make sure we can do that before
- # succeeding.
- new_firstline = _RemoveNamespacePrefix(firstline.line, namespace_prefix)
- if new_firstline:
- assert all_lines[first_contentful_line] == firstline.line
- all_lines[first_contentful_line] = new_firstline
- reorder_span = possible_reorder_span
+ namespace_reorder_spans = _GetNamespaceLevelReorderSpans(file_lines)
+ for namespace_prefix, possible_reorder_span in namespace_reorder_spans:
+ if (namespace_prefix and possible_reorder_span and
+ firstline.line.startswith(namespace_prefix)):
+ # Great, we can go into this reorder_span. We also need to
+ # modify all-lines because this line doesn't need the
+ # namespace prefix anymore. Make sure we can do that before
+ # succeeding.
+ new_firstline = _RemoveNamespacePrefix(firstline.line, namespace_prefix)
+ if new_firstline:
+ assert all_lines[first_contentful_line] == firstline.line
+ all_lines[first_contentful_line] = new_firstline
+ sort_key = re.sub(r'\s+', '', new_firstline)
+ reorder_span = possible_reorder_span
+ break
# If that didn't work out, find a top-level reorder span to go into.
if reorder_span is None:
@@ -1944,7 +2021,9 @@ def _GetSymbolNameFromForwardDeclareLine(line):
"""
iwyu_namespace_re = re.compile(r'namespace ([^{]*) { ')
symbolname_re = re.compile(r'([A-Za-z0-9_]+)')
- namespaces_in_line = iwyu_namespace_re.findall(line)
+ # Turn anonymous namespaces into their proper symbol representation.
+ namespaces_in_line = iwyu_namespace_re.findall(line.replace(
+ "namespace {", "namespace (anonymous namespace) {"))
symbols_in_line = symbolname_re.findall(line)
symbol_name = symbols_in_line[-1]
if (namespaces_in_line):
diff --git a/fix_includes_test.py b/fix_includes_test.py
index dc7aed3..9fe63ba 100755
--- a/fix_includes_test.py
+++ b/fix_includes_test.py
@@ -1099,20 +1099,14 @@ template <typename T> class Baz; // lines 9-9
self.RegisterFileContents({'add_fwd_decl_with_hdr_guard': infile})
self.ProcessAndTest(iwyu_output)
- def testDoNotAddForwardDeclareInsideNamespaceWithContentfulLine(self):
- """Tests that for 'confusing' code, we keep fwd-decl at the top."""
+ def testAddForwardDeclareInsideNamespaceWithIfDef(self):
+ """Tests that ifdef blocks are ignored when finding namespaces."""
infile = """\
// Copyright 2010
#include "foo.h"
class Bar;
-///+namespace ns {
-///+namespace ns2 {
-///+class NsBang;
-///+template <typename T> class NsBaz;
-///+} // namespace ns2
-///+} // namespace ns
template <typename T> class Baz;
#ifdef THIS_IS_A_CONTENTFUL_LINE
@@ -1123,8 +1117,10 @@ namespace ns {
namespace ns2 {
+///+class NsBang;
class NsFoo;
template <typename T> class NsBar;
+///+template <typename T> class NsBaz;
}
@@ -1133,13 +1129,13 @@ template <typename T> class NsBar;
int main() { return 0; }
"""
iwyu_output = """\
-do_not_add_fwd_decl_inside_namespace should add these lines:
+add_forward_declares_after_ifdef_code should add these lines:
namespace ns { namespace ns2 { class NsBang; } }
namespace ns { namespace ns2 { template <typename T> class NsBaz; } }
-do_not_add_fwd_decl_inside_namespace should remove these lines:
+add_forward_declares_after_ifdef_code should remove these lines:
-The full include-list for do_not_add_fwd_decl_inside_namespace:
+The full include-list for add_forward_declares_after_ifdef_code:
#include "foo.h" // lines 3-3
class Bar; // lines 5-5
namespace ns { namespace ns2 { class NsBang; } }
@@ -1149,7 +1145,7 @@ namespace ns { namespace ns2 { template <typename T> class NsBaz; } }
template <typename T> class Baz; // lines 6-6
---
"""
- self.RegisterFileContents({'do_not_add_fwd_decl_inside_namespace': infile})
+ self.RegisterFileContents({'add_forward_declares_after_ifdef_code': infile})
self.ProcessAndTest(iwyu_output)
def testAddForwardDeclareInsideNamespaceWithoutForwardDeclaresAlready(self):
@@ -1165,7 +1161,7 @@ template <typename T> class Baz;
namespace ns {
namespace ns2 { // we sure do love nesting our namespaces!
-
+///-
///+namespace ns3 {
///+class NsBang;
///+template <typename T> class NsBaz;
@@ -1197,6 +1193,40 @@ template <typename T> class Baz; // lines 6-6
infile})
self.ProcessAndTest(iwyu_output)
+ def testAddForwardDeclareInsideNamespaceWithCompactEndings(self):
+ """Tests we put fwd-decls inside an ns when using compact namespace endings."""
+ infile = """\
+// Copyright 2010
+
+namespace ns { namespace ns1 { namespace ns2 {
+class Ns2Bang;
+}} // namespace ns2 // namespace ns1
+///+class NsBar;
+class NsBaz;
+ ///-
+namespace ns3 { namespace ns4 { ///-
+class Ns4Bye; ///-
+}} // namespace ns4 // namespace ns3 ///-
+} // namespace ns
+
+int main() { return 0; }
+"""
+ iwyu_output = """\
+add_fwd_decl_inside_namespace_without_compact_endings should add these lines:
+namespace ns { class NsBar; }
+
+add_fwd_decl_inside_namespace_without_compact_endings should remove these lines:
+- namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { namespace ns4 { class Ns4Bye; } } } } } // lines 9-9
+
+The full include-list for add_fwd_decl_inside_namespace_without_compact_endings:
+namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; } } } // lines 4-4
+namespace ns { class NsBaz; } // lines 6-6
+---
+"""
+ self.RegisterFileContents({'add_fwd_decl_inside_namespace_without_compact_endings':
+ infile})
+ self.ProcessAndTest(iwyu_output)
+
def testAddForwardDeclareInsideNamespaceWithUnnamedNamespace(self):
"""Tests that unnamed namespaces do not mess up our in-ns calculation."""
infile = """\
@@ -1207,7 +1237,6 @@ template <typename T> class Baz; // lines 6-6
class Bar;
namespace ns {
-///+
///+class NsBang;
///+template <typename T> class NsBaz;
@@ -1229,13 +1258,248 @@ add_fwd_decl_inside_namespace_unnamed_ns should remove these lines:
The full include-list for add_fwd_decl_inside_namespace_unnamed_ns:
#include "foo.h" // lines 3-3
class Bar; // lines 5-5
+namespace ns { namespace { class NsFoo; } } // lines 10-10
+namespace ns { class NsBang; }
+namespace ns { template <typename T> class NsBaz; }
+namespace ns { namespace { template <typename T> class NsBar; } } // lines 11-11
+---
+"""
+ self.RegisterFileContents({'add_fwd_decl_inside_namespace_unnamed_ns':
+ infile})
+ self.ProcessAndTest(iwyu_output)
+
+ def testAddForwardDeclareInsideNamespacesWithUnnamedNamespaceAndContent(self):
+ """Tests that nested namespaces with forward declares still get new additions."""
+ infile = """\
+// Copyright 2010
+
+#include "foo.h"
+
+class Bar;
+///+class Baz;
+
+namespace ns {
+///+class NsBang;
+///+template <typename T> class NsBaz;
+
+namespace {
+///+class NsBaz;
+class NsFoo;
+template <typename T> class NsBar;
+}
+
+namespace ns1 {
+///+class Ns1Bar;
+///+class Ns1Baz;
+class Ns1Foo;
+
+int ns_int = 5; // here's my contentful line
+}
+}
+
+int main() { return 0; }
+"""
+ iwyu_output = """\
+add_fwd_decl_inside_namespaces_with_existing_content should add these lines:
+class Baz;
+namespace ns { class NsBang; }
+namespace ns { template <typename T> class NsBaz; }
+namespace ns { namespace { class NsBaz; } }
+namespace ns { namespace ns1 { class Ns1Bar; } }
+namespace ns { namespace ns1 { class Ns1Baz; } }
+
+add_fwd_decl_inside_namespaces_with_existing_content should remove these lines:
+
+The full include-list for add_fwd_decl_inside_namespaces_with_existing_content:
+#include "foo.h" // lines 3-3
+class Bar; // lines 5-5
+class Baz;
+namespace ns { namespace { class NsFoo; } } // lines 10-10
+namespace ns { namespace { class NsBaz; } }
+namespace ns { class NsBang; }
+namespace ns { template <typename T> class NsBaz; }
+namespace ns { namespace { template <typename T> class NsBar; } } // lines 11-11
+namespace ns { namespace ns1 { class Ns1Foo; } } // lines 15-15
+namespace ns { namespace ns1 { class Ns1Bar; } }
+namespace ns { namespace ns1 { class Ns1Baz; } }
+---
+"""
+ self.RegisterFileContents({'add_fwd_decl_inside_namespaces_with_existing_content':
+ infile})
+ self.ProcessAndTest(iwyu_output)
+
+ def testAddForwardDeclareInsideAllmanNamespacesWithUnnamedNamespaceAndContent(self):
+ """Tests that nested Allman namespaces with forward declares still get new additions."""
+ infile = """\
+// Copyright 2010
+
+#include "foo.h"
+
+class Bar;
+///+class Baz;
+
+namespace ns
+{
+///+class NsBang;
+///+template <typename T> class NsBaz;
+
+namespace
+{
+///+class NsBaz;
+class NsFoo;
+template <typename T> class NsBar;
+}
+
+namespace ns1
+{
+///+class Ns1Bar;
+///+class Ns1Baz;
+class Ns1Foo;
+
+int ns_int = 5; // here's my contentful line
+}
+}
+
+int main() { return 0; }
+"""
+ iwyu_output = """\
+add_fwd_decl_inside_allman_namespaces_with_existing_content should add these lines:
+class Baz;
+namespace ns { class NsBang; }
+namespace ns { template <typename T> class NsBaz; }
+namespace ns { namespace { class NsBaz; } }
+namespace ns { namespace ns1 { class Ns1Bar; } }
+namespace ns { namespace ns1 { class Ns1Baz; } }
+
+add_fwd_decl_inside_allman_namespaces_with_existing_content should remove these lines:
+
+The full include-list for add_fwd_decl_inside_allman_namespaces_with_existing_content:
+#include "foo.h" // lines 3-3
+class Bar; // lines 5-5
+class Baz;
namespace ns { namespace { class NsFoo; } } // lines 12-12
+namespace ns { namespace { class NsBaz; } }
namespace ns { class NsBang; }
namespace ns { template <typename T> class NsBaz; }
namespace ns { namespace { template <typename T> class NsBar; } } // lines 13-13
+namespace ns { namespace ns1 { class Ns1Foo; } } // lines 18-18
+namespace ns { namespace ns1 { class Ns1Bar; } }
+namespace ns { namespace ns1 { class Ns1Baz; } }
---
"""
- self.RegisterFileContents({'add_fwd_decl_inside_namespace_unnamed_ns':
+ self.RegisterFileContents({'add_fwd_decl_inside_allman_namespaces_with_existing_content':
+ infile})
+ self.ProcessAndTest(iwyu_output)
+
+ def testAddForwardDeclareInsideMixedNamespacesWithUnnamedNamespaceAndContent(self):
+ """Tests that nested mixed namespaces with forward declares still get new additions."""
+ infile = """\
+// Copyright 2010
+
+#include "bar.h"
+///+
+///+class Baz;
+///+
+namespace ns { namespace ns1 { namespace ns2
+{
+///+class Ns2Bang;
+///+template <typename T> class Ns2Baz;
+///+
+namespace
+{
+///+class NsaBaz;
+class NsaFoo;
+template <typename T> class NsaBar;
+} // namespace
+namespace ns3 {
+///+class Ns3Bar;
+///+class Ns3Baz;
+class Ns3Foo;
+///+
+int ns3_int = 5; // here's my contentful line
+} // namespace ns3
+} // namespace ns2
+} // namespace ns1
+} // namespace ns
+
+int main() { return 0; }
+"""
+ iwyu_output = """\
+add_fwd_decl_inside_mixed_namespaces_with_existing_content should add these lines:
+class Baz;
+namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; } } }
+namespace ns { namespace ns1 { namespace ns2 { template <typename T> class Ns2Baz; } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace { class NsaBaz; } } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Bar; } } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Baz; } } } }
+
+add_fwd_decl_inside_mixed_namespaces_with_existing_content should remove these lines:
+
+The full include-list for add_fwd_decl_inside_mixed_namespaces_with_existing_content:
+#include "bar.h" // lines 3-3
+#include "foo.h"
+class Baz;
+namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; } } }
+namespace ns { namespace ns1 { namespace ns2 { template <typename T> class Ns2Baz; } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace { class NsaFoo; } } } } // lines 8-8
+namespace ns { namespace ns1 { namespace ns2 { namespace { template <typename T> class NsaBar; } } } } // lines 9-9
+namespace ns { namespace ns1 { namespace ns2 { namespace { class NsaBaz; } } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Bar; } } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Baz; } } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Foo; } } } } // lines 12-12
+---
+"""
+ self.RegisterFileContents({'add_fwd_decl_inside_mixed_namespaces_with_existing_content':
+ infile})
+ self.ProcessAndTest(iwyu_output)
+
+ def testAddForwardDeclareInsideNestedNamespacesAndTopLevelForComplexNamespaces(self):
+ """Tests that nested namespaces still get new additions while putting hard to resolve forward declares at the top."""
+ infile = """\
+// Copyright 2010
+
+///+namespace ns {
+///+class NsBang;
+///+namespace ns1 {
+///+Ns1Bang;
+///+} // namespace ns1
+///+} // namespace ns
+///+
+namespace ns { namespace ns1 { namespace ns2 {
+class Ns2Bang;
+class Ns2Bar; ///-
+///+class Ns2Baz;
+///+
+namespace ns3 {
+///+class Ns3Bang;
+class Ns3Baz;
+} // namespace ns3
+} // namespace ns2
+} // namespace ns1
+} // namespace ns
+
+int main() { return 0; }
+"""
+ iwyu_output = """\
+add_fwd_decl_inside_nested_namespaces_and_top_level_for_complex_namespaces should add these lines:
+namespace ns { class NsBang; }
+namespace ns { namespace ns1 { Ns1Bang; }
+namespace ns { namespace ns1 { namespace ns2 { class Ns2Baz; } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Bang; } } } }
+
+add_fwd_decl_inside_nested_namespaces_and_top_level_for_complex_namespaces should remove these lines:
+- namespace ns { namespace ns1 { namespace ns2 { class Ns2Bar; } } } // lines 5-5
+
+The full include-list for add_fwd_decl_inside_nested_namespaces_and_top_level_for_complex_namespaces:
+namespace ns { class NsBang; }
+namespace ns { namespace ns1 { Ns1Bang; }
+namespace ns { namespace ns1 { namespace ns2 { class Ns2Bang; } } } // lines 4-4
+namespace ns { namespace ns1 { namespace ns2 { class Ns2Baz; } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Bang; } } } }
+namespace ns { namespace ns1 { namespace ns2 { namespace ns3 { class Ns3Baz; } } } } // lines 7-7
+---
+"""
+ self.RegisterFileContents({'add_fwd_decl_inside_nested_namespaces_and_top_level_for_complex_namespaces':
infile})
self.ProcessAndTest(iwyu_output)
@@ -2454,12 +2718,12 @@ The full include-list for simple_with_comment.h:
#define IDENTIFYING_HEADER_GUARD_LINES_H_
namespace foo {
-// The namespace decl should come before this #define, not after.
-// It will, unless we wrongly say the #define is a header-guard define.
///+namespace bar {
///+class Baz;
///+} // namespace bar
///+
+// The namespace decl should come before this #define, not after.
+// It will, unless we wrongly say the #define is a header-guard define.
#define NOT_A_HEADER_GUARD_LINE 1
}
@@ -3630,7 +3894,7 @@ template <typename T> class Baz;
namespace ns {
-
+///-
///+class NsFoo;
///+namespace ns2 { namespace ns3 { class NsBaz; } }
///+namespace ns2 { namespace ns3 { template <typename T> class NsBang; } }