diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2019-01-31 17:22:54 +0100 |
---|---|---|
committer | Kim Gräsman <kim.grasman@gmail.com> | 2019-02-06 19:55:19 +0100 |
commit | 353a6da9b711cbee1f3261c8e19de22807806932 (patch) | |
tree | bec6f1e73970bc5a448d09411b7836b4849f2196 | |
parent | 8b63d319e9ff0829b905b013002af6c8048b564c (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-x | fix_includes_test.py | 43 | ||||
-rw-r--r-- | iwyu_globals.cc | 6 | ||||
-rw-r--r-- | iwyu_globals.h | 1 | ||||
-rw-r--r-- | iwyu_output.cc | 47 | ||||
-rwxr-xr-x | run_iwyu_tests.py | 3 | ||||
-rw-r--r-- | tests/cxx/cxx17ns-i1.h | 34 | ||||
-rw-r--r-- | tests/cxx/cxx17ns.cc | 36 |
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 */ |