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.
This commit is contained in:
parent
8b63d319e9
commit
353a6da9b7
|
@ -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 = """\
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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_
|
|
@ -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 */
|
Loading…
Reference in New Issue