Add support for group/backreferences regex replacement in mapping files.

This is useful to generically remap files from one directory to another.
The following mapping would for instance map all files under the "foo/"
directory to "third_party/foo/", for instance, mapping "foo/bar.h" to
"third_party/foo/bar.h".

[ { include: ['@"(foo/.*)"', private, '"third_party/\1"', public ] } ]

This is useful for large project where different third_party libraries
are compiled together. In Chromium for instance, include paths would
look like:
 -Ithird_party -Ithird_party/v8/include

This allows code in V8 to include its own headers directly. Other
third_parties would include them via "third_party/v8/include/...".
Because files are accessible from two different paths, IWYU can't know
which should be used. Using a mapping file, adding or removing the
"third_party/v8/include" prefix where needed, resolves this problem.
This commit is contained in:
Jean-Philippe Gravel 2022-10-07 10:41:07 -04:00 committed by Kim Gräsman
parent b74819dc5f
commit 6fb66575fb
7 changed files with 120 additions and 2 deletions

View File

@ -1436,10 +1436,14 @@ void IncludePicker::ExpandRegexes() {
for (const auto& incmap : quoted_includes_to_quoted_includers_) {
const string& hdr = incmap.first;
for (const string& regex_key : filepath_include_map_regex_keys) {
const string regex = regex_key.substr(1);
const vector<MappedInclude>& map_to = filepath_include_map_[regex_key];
if (RegexMatch(regex_dialect, hdr, regex_key.substr(1)) &&
if (RegexMatch(regex_dialect, hdr, regex) &&
!ContainsQuotedInclude(map_to, hdr)) {
Extend(&filepath_include_map_[hdr], filepath_include_map_[regex_key]);
for (const MappedInclude& target : map_to) {
filepath_include_map_[hdr].push_back(MappedInclude(
RegexReplace(regex_dialect, hdr, regex, target.quoted_include)));
}
MarkVisibility(&include_visibility_map_, hdr,
include_visibility_map_[regex_key]);
}

View File

@ -46,4 +46,26 @@ bool RegexMatch(RegexDialect dialect, const std::string& str,
CHECK_UNREACHABLE_("Unexpected regex dialect");
}
std::string RegexReplace(RegexDialect dialect, const std::string& str,
const std::string& pattern,
const std::string& replacement) {
switch (dialect) {
case RegexDialect::LLVM: {
// llvm::Regex::sub has search semantics. Enclose the pattern in ^...$
// for start/end anchoring.
llvm::Regex r("^" + pattern + "$");
return r.sub(replacement, str);
}
case RegexDialect::ECMAScript: {
// std::regex_replace has search semantics. Enclose the pattern in ^...$
// for start/end anchoring.
std::regex r("^" + pattern + "$", std::regex_constants::ECMAScript);
return std::regex_replace(str, r, replacement,
std::regex_constants::format_first_only);
}
}
CHECK_UNREACHABLE_("Unexpected regex dialect");
}
} // namespace include_what_you_use

View File

@ -23,6 +23,12 @@ bool ParseRegexDialect(const char* str, RegexDialect* dialect);
bool RegexMatch(RegexDialect dialect, const std::string& str,
const std::string& pattern);
// Returns input string with the first match of pattern replaced, for the given
// regex dialect.
std::string RegexReplace(RegexDialect dialect, const std::string& str,
const std::string& pattern,
const std::string& replacement);
} // namespace include_what_you_use
#endif // INCLUDE_WHAT_YOU_USE_IWYU_REGEX_H_

View File

@ -0,0 +1,38 @@
//===--- mapping_replace_regex_ecmascript.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.
//
//===----------------------------------------------------------------------===//
// IWYU_ARGS: -I . \
// -Xiwyu --regex=ecmascript \
// -Xiwyu --mapping_file=tests/cxx/mapping_replace_regex_ecmascript.imp
// Generically map include paths to a different include directory:
// * The include of tests/cxx/direct.h should nominally be replaced by
// tests/cxx/indirect.h, where IndirectClass is defined
// * But we provide a mapping adding a "foobar" prefix to any include under
// "tests/cxx", resulting in suggesting foobar/tests/cxx/indirect.h.
#include "tests/cxx/direct.h"
void f() {
// IWYU: IndirectClass is defined in "foobar/tests/cxx/indirect.h"
IndirectClass i;
}
/**** IWYU_SUMMARY
tests/cxx/mapping_replace_regex_ecmascript.cc should add these lines:
#include "foobar/tests/cxx/indirect.h"
tests/cxx/mapping_replace_regex_ecmascript.cc should remove these lines:
- #include "tests/cxx/direct.h" // lines XX-XX
The full include-list for tests/cxx/mapping_replace_regex_ecmascript.cc:
#include "foobar/tests/cxx/indirect.h" // for IndirectClass
***** IWYU_SUMMARY */

View File

@ -0,0 +1,5 @@
# Maps headers to a different include path.
# The foobar directory does not exist; we just want IWYU to suggest using it.
[
{ include: ['@"(tests/cxx/.*)"', private, '"foobar/$1"', public ] },
]

View File

@ -0,0 +1,38 @@
//===--- mapping_replace_regex_llvm.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.
//
//===----------------------------------------------------------------------===//
// IWYU_ARGS: -I . \
// -Xiwyu --regex=llvm \
// -Xiwyu --mapping_file=tests/cxx/mapping_replace_regex_llvm.imp
// Generically map include paths to a different include directory:
// * The include of tests/cxx/direct.h should nominally be replaced by
// tests/cxx/indirect.h, where IndirectClass is defined
// * But we provide a mapping adding a "foobar" prefix to any include under
// "tests/cxx", resulting in suggesting foobar/tests/cxx/indirect.h.
#include "tests/cxx/direct.h"
void f() {
// IWYU: IndirectClass is defined in "foobar/tests/cxx/indirect.h"
IndirectClass i;
}
/**** IWYU_SUMMARY
tests/cxx/mapping_replace_regex_llvm.cc should add these lines:
#include "foobar/tests/cxx/indirect.h"
tests/cxx/mapping_replace_regex_llvm.cc should remove these lines:
- #include "tests/cxx/direct.h" // lines XX-XX
The full include-list for tests/cxx/mapping_replace_regex_llvm.cc:
#include "foobar/tests/cxx/indirect.h" // for IndirectClass
***** IWYU_SUMMARY */

View File

@ -0,0 +1,5 @@
# Maps headers to a different include path.
# The foobar directory does not exist; we just want IWYU to suggest using it.
[
{ include: ['@"(tests/cxx/.*)"', private, '"foobar/\1"', public ] },
]