summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2019-01-31 17:22:54 +0100
committerKim Gräsman <kim.grasman@gmail.com>2019-02-06 19:55:19 +0100
commit353a6da9b711cbee1f3261c8e19de22807806932 (patch)
treebec6f1e73970bc5a448d09411b7836b4849f2196
parent8b63d319e9ff0829b905b013002af6c8048b564c (diff)
Add new --cxx17ns option
This opts in for the more concise syntax introduced in C++17: namespace a::b { ... }. Usage of this is especially useful in codebases where existing forward declarations in nested namespaces already use this form: so the IWYU suggestion for new forward declarations can be consistent with the existing ones. fix_includes.py already handled this, but add a test to maintain this behavior, too.
-rwxr-xr-xfix_includes_test.py43
-rw-r--r--iwyu_globals.cc6
-rw-r--r--iwyu_globals.h1
-rw-r--r--iwyu_output.cc47
-rwxr-xr-xrun_iwyu_tests.py3
-rw-r--r--tests/cxx/cxx17ns-i1.h34
-rw-r--r--tests/cxx/cxx17ns.cc36
7 files changed, 163 insertions, 7 deletions
diff --git a/fix_includes_test.py b/fix_includes_test.py
index 9433d7d..36a567d 100755
--- a/fix_includes_test.py
+++ b/fix_includes_test.py
@@ -384,6 +384,49 @@ The full include-list for empty_namespace:
self.RegisterFileContents({'empty_namespace': infile})
self.ProcessAndTest(iwyu_output)
+ def testCXX17NS(self):
+ """Tests handling of output using the --cxx17ns switch."""
+ infile = """\
+#include "cxx17ns-i1.h"///-
+///+
+///+namespace a::b::c {
+///+struct One;
+///+} // namespace a::b::c
+///+namespace a::b {
+///+struct One2;
+///+} // namespace a::b
+///+namespace a {
+///+struct One4;
+///+struct One3;
+///+} // namespace a
+
+struct Two {
+ Two(a::b::c::One& one);
+ Two(a::b::One2& one);
+ Two(a::One3& one);
+ Two(a::One4& one);
+};
+"""
+ iwyu_output = """\
+cxx17ns.cc should add these lines:
+namespace a { namespace { struct One4; } }
+namespace a { struct One3; }
+namespace a::b { struct One2; }
+namespace a::b::c { struct One; }
+
+cxx17ns.cc should remove these lines:
+- #include "cxx17ns-i1.h" // lines 1-1
+
+The full include-list for cxx17ns.cc:
+namespace a { namespace { struct One4; } }
+namespace a { struct One3; }
+namespace a::b { struct One2; }
+namespace a::b::c { struct One; }
+---
+"""
+ self.RegisterFileContents({'cxx17ns.cc': infile})
+ self.ProcessAndTest(iwyu_output)
+
def testRemovePartOfEmptyNamespace(self):
"""Tests we remove a namespace if empty, but not enclosing namespaces."""
infile = """\
diff --git a/iwyu_globals.cc b/iwyu_globals.cc
index 02a2fe5..59c31c9 100644
--- a/iwyu_globals.cc
+++ b/iwyu_globals.cc
@@ -96,6 +96,7 @@ static void PrintHelp(const char* extra_msg) {
" --verbose=<level>: the higher the level, the more output.\n"
" --quoted_includes_first: when sorting includes, place quoted\n"
" ones first.\n"
+ " --cxx17ns: suggests the more concise syntax introduced in C++17\n"
"\n"
"In addition to IWYU-specific options you can specify the following\n"
"options without -Xiwyu prefix:\n"
@@ -165,7 +166,8 @@ CommandlineFlags::CommandlineFlags()
pch_in_code(false),
no_comments(false),
no_fwd_decls(false),
- quoted_includes_first(false) {
+ quoted_includes_first(false),
+ cxx17ns(false) {
}
int CommandlineFlags::ParseArgv(int argc, char** argv) {
@@ -183,6 +185,7 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) {
{"no_comments", optional_argument, nullptr, 'o'},
{"no_fwd_decls", optional_argument, nullptr, 'f'},
{"quoted_includes_first", no_argument, nullptr, 'q' },
+ {"cxx17ns", no_argument, nullptr, 'C'},
{nullptr, 0, nullptr, 0}
};
static const char shortopts[] = "d::p:v:c:m:n";
@@ -215,6 +218,7 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) {
CHECK_((max_line_length >= 0) && "Max line length must be positive");
break;
case 'q': quoted_includes_first = true; break;
+ case 'C': cxx17ns = true; break;
case -1: return optind; // means 'no more input'
default:
PrintHelp("FATAL ERROR: unknown flag.");
diff --git a/iwyu_globals.h b/iwyu_globals.h
index fa333fd..5293f13 100644
--- a/iwyu_globals.h
+++ b/iwyu_globals.h
@@ -102,6 +102,7 @@ struct CommandlineFlags {
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 cxx17ns; // -C: C++17 nested namespace syntax
};
const CommandlineFlags& GlobalFlags();
diff --git a/iwyu_output.cc b/iwyu_output.cc
index faee317..61a956e 100644
--- a/iwyu_output.cc
+++ b/iwyu_output.cc
@@ -346,12 +346,27 @@ string PrintablePtr(const void* ptr) {
// `-- TagDecl (class, struct, union, enum)
// `-- RecordDecl (class, struct, union)
+// Determines if a NamedDecl has any parent namespace, which is anonymous.
+bool HasAnonymousNamespace(const NamedDecl* decl) {
+ for (const DeclContext* ctx = decl->getDeclContext();
+ ctx && isa<NamedDecl>(ctx); ctx = ctx->getParent()) {
+ if (const NamespaceDecl* ns = DynCastFrom(ctx)) {
+ if (ns->isAnonymousNamespace()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
// Given a NamedDecl that presents a (possibly template) record
// (i.e. class, struct, or union) type declaration, and the print-out
// of its (possible) template parameters and kind (e.g. "template
// <typename T> struct"), returns its forward declaration line.
string PrintForwardDeclare(const NamedDecl* decl,
- const string& tpl_params_and_kind) {
+ const string& tpl_params_and_kind,
+ bool cxx17ns) {
// We need to short-circuit the logic for testing.
if (const FakeNamedDecl* fake = FakeNamedDeclIfItIsOne(decl)) {
return tpl_params_and_kind + " " + fake->qual_name() + ";";
@@ -362,19 +377,39 @@ string PrintForwardDeclare(const NamedDecl* decl,
std::string fwd_decl = std::string(decl->getName()) + ";";
bool seen_namespace = false;
+ // Anonymous namespaces are not using the more concise syntax.
+ bool concat_namespaces = cxx17ns && !HasAnonymousNamespace(decl);
for (const DeclContext* ctx = decl->getDeclContext();
ctx && isa<NamedDecl>(ctx); ctx = ctx->getParent()) {
if (const RecordDecl* rec = DynCastFrom(ctx)) {
fwd_decl = std::string(rec->getName()) + "::" + fwd_decl;
} else if (const NamespaceDecl* ns = DynCastFrom(ctx)) {
+ bool first = !seen_namespace;
if (!seen_namespace) {
seen_namespace = true;
fwd_decl = tpl_params_and_kind + " " + fwd_decl;
}
- const std::string ns_name = ns->isAnonymousNamespace() ?
- "" : (std::string(ns->getName()) + " ");
- fwd_decl = "namespace " + ns_name + "{ " + fwd_decl + " }";
+ if (concat_namespaces) {
+ std::string ns_name = std::string(ns->getName());
+ std::string prefix = ns_name;
+ std::string suffix;
+ if (first) {
+ first = false;
+ prefix = prefix + " { ";
+ }
+ if (ctx->getParent() && isa<NamedDecl>(ctx->getParent())) {
+ prefix = "::" + prefix;
+ } else {
+ prefix = "namespace " + prefix;
+ suffix = " }";
+ }
+ fwd_decl = prefix + fwd_decl + suffix;
+ } else {
+ std::string ns_name = ns->isAnonymousNamespace() ?
+ std::string() : (std::string(ns->getName()) + " ");
+ fwd_decl = "namespace " + ns_name + "{ " + fwd_decl + " }";
+ }
} else if (const FunctionDecl* fn = DynCastFrom(ctx)) {
// A local class (class defined inside a function).
fwd_decl = std::string(fn->getName()) + "::" + fwd_decl;
@@ -392,7 +427,7 @@ string PrintForwardDeclare(const NamedDecl* decl,
// Given a RecordDecl, return the line that could be put in source
// code to forward-declare the record type, e.g. "namespace ns { class Foo; }".
string MungedForwardDeclareLineForNontemplates(const RecordDecl* decl) {
- return PrintForwardDeclare(decl, GetKindName(decl));
+ return PrintForwardDeclare(decl, GetKindName(decl), GlobalFlags().cxx17ns);
}
// Given a TemplateDecl representing a class|struct|union template
@@ -421,7 +456,7 @@ string MungedForwardDeclareLineForTemplates(const TemplateDecl* decl) {
// argument is inclusive, so substract one to get past the end-space.
const string::size_type name = line.rfind(' ', endpos - 1);
CHECK_(name != string::npos && "Unexpected printable template-type");
- return PrintForwardDeclare(decl, line.substr(0, name));
+ return PrintForwardDeclare(decl, line.substr(0, name), GlobalFlags().cxx17ns);
}
string MungedForwardDeclareLine(const NamedDecl* decl) {
diff --git a/run_iwyu_tests.py b/run_iwyu_tests.py
index 33798ae..f15ce19 100755
--- a/run_iwyu_tests.py
+++ b/run_iwyu_tests.py
@@ -85,6 +85,7 @@ class OneIwyuTest(unittest.TestCase):
'prefix_header_includes_remove.cc': ['--prefix_header_includes=remove'],
'prefix_header_operator_new.cc': ['--prefix_header_includes=remove'],
'quoted_includes_first.cc': ['--pch_in_code', '--quoted_includes_first'],
+ 'cxx17ns.cc': ['--cxx17ns'],
}
prefix_headers = [self.Include('prefix_header_includes-d1.h'),
self.Include('prefix_header_includes-d2.h'),
@@ -113,6 +114,7 @@ class OneIwyuTest(unittest.TestCase):
'range_for.cc': ['-std=c++11'],
'typedef_in_template.cc': ['-std=c++11'],
'inheriting_ctor.cc': ['-std=c++11'],
+ 'cxx17ns.cc': ['-std=c++17'],
}
include_map = {
'alias_template.cc': ['.'],
@@ -193,6 +195,7 @@ class OneIwyuTest(unittest.TestCase):
'using_aliased_symbol_unused.cc': ['.'],
'varargs_and_references.cc': ['.'],
'virtual_tpl_method.cc': ['.'],
+ 'cxx17ns.cc': ['.'],
}
# Internally, we like it when the paths start with rootdir.
self._iwyu_flags_map = dict((posixpath.join(self.rootdir, k), v)
diff --git a/tests/cxx/cxx17ns-i1.h b/tests/cxx/cxx17ns-i1.h
new file mode 100644
index 0000000..8e718a2
--- /dev/null
+++ b/tests/cxx/cxx17ns-i1.h
@@ -0,0 +1,34 @@
+//===--- cxx17ns-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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef INCLUDE_WHAT_YOU_USE_TESTS_CXX_CXX17NS_I1_H_
+#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_CXX17NS_I1_H_
+
+namespace a {
+namespace b {
+namespace c {
+struct One {
+ One();
+};
+} // namespace c
+struct One2 {
+ One2();
+};
+} // namespace b
+struct One3 {
+ One3();
+};
+namespace {
+struct One4 {
+ One4();
+};
+}
+} // namespace a
+
+#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_CXX17NS_I1_H_
diff --git a/tests/cxx/cxx17ns.cc b/tests/cxx/cxx17ns.cc
new file mode 100644
index 0000000..8782b5e
--- /dev/null
+++ b/tests/cxx/cxx17ns.cc
@@ -0,0 +1,36 @@
+//===--- cxx17ns.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/cxx17ns-i1.h"
+
+struct Two {
+ Two(a::b::c::One& one);
+ Two(a::b::One2& one);
+ Two(a::One3& one);
+ Two(a::One4& one);
+};
+
+/**** IWYU_SUMMARY
+
+tests/cxx/cxx17ns.cc should add these lines:
+namespace a { namespace { struct One4; } }
+namespace a { struct One3; }
+namespace a::b { struct One2; }
+namespace a::b::c { struct One; }
+
+tests/cxx/cxx17ns.cc should remove these lines:
+- #include "tests/cxx/cxx17ns-i1.h" // lines XX-XX
+
+The full include-list for tests/cxx/cxx17ns.cc:
+namespace a { namespace { struct One4; } }
+namespace a { struct One3; }
+namespace a::b { struct One2; }
+namespace a::b::c { struct One; }
+
+***** IWYU_SUMMARY */