summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Bytheway <jbytheway@gmail.com>2019-08-19 15:57:58 -0400
committerKim Gräsman <kim.grasman@gmail.com>2019-09-01 16:41:53 +0200
commit7662c814a4b4590057c03a84c3235f0a20a81b22 (patch)
treea935f655951eff9175a3b5dd26bf03062c700ecd
parent2bd1ddee4a38152462c78abaa930d5a4c9604ea0 (diff)
Fix nested exports via relative includes
The keys in the filepath_include_map_ are quoted includes. When these are relative includes they are context-dependent, which means they might not match the values elsewhere in the map. That can cause the transitive closure operation on the map (to resolve exports of exports) to fail. When adding exports from pragmas, try adding them with two different keys -- the quoted include as written, and the quoted include as deduced from the filename. We need the former to match mapping files and the latter to match paths generated within the code. Having both should make both uses cases work.
-rw-r--r--iwyu_include_picker.cc13
-rw-r--r--iwyu_preprocessor.cc17
-rw-r--r--tests/cxx/relative_include_of_double_export-d1.h15
-rw-r--r--tests/cxx/relative_include_of_double_export.cc23
4 files changed, 58 insertions, 10 deletions
diff --git a/iwyu_include_picker.cc b/iwyu_include_picker.cc
index dcb06a8..22a104e 100644
--- a/iwyu_include_picker.cc
+++ b/iwyu_include_picker.cc
@@ -1376,18 +1376,19 @@ vector<string> IncludePicker::GetCandidateHeadersForSymbolUsedFrom(
vector<MappedInclude> IncludePicker::GetCandidateHeadersForFilepath(
const string& filepath, const string& including_filepath) const {
CHECK_(has_called_finalize_added_include_lines_ && "Must finalize includes");
+ string absolute_quoted_header = ConvertToQuotedInclude(filepath);
+ vector<MappedInclude> retval =
+ GetPublicValues(filepath_include_map_, absolute_quoted_header);
+
+ // We also need to consider the header itself. Make that an option if it's
+ // public or there's no other option.
string quoted_header;
if (including_filepath.empty()) {
- quoted_header = ConvertToQuotedInclude(filepath);
+ quoted_header = absolute_quoted_header;
} else {
quoted_header = ConvertToQuotedInclude(
filepath, MakeAbsolutePath(GetParentPath(including_filepath)));
}
- vector<MappedInclude> retval =
- GetPublicValues(filepath_include_map_, quoted_header);
-
- // We also need to consider the header itself. Make that an option if it's
- // public or there's no other option.
MappedInclude default_header(quoted_header, filepath);
if (retval.empty() || GetVisibility(default_header, kPublic) == kPublic) {
// Insert at front so it's the preferred option
diff --git a/iwyu_preprocessor.cc b/iwyu_preprocessor.cc
index ff93d39..5052dc6 100644
--- a/iwyu_preprocessor.cc
+++ b/iwyu_preprocessor.cc
@@ -412,12 +412,21 @@ void IwyuPreprocessorInfo::MaybeProtectInclude(
protect_reason = "pragma_export";
const string includer_path = GetFilePath(includer);
const string quoted_includer = ConvertToQuotedInclude(includer_path);
- MutableGlobalIncludePicker()->AddMapping(
- include_name_as_written,
- MappedInclude(quoted_includer, includer_path));
+ MappedInclude map_to(quoted_includer, includer_path);
+ MutableGlobalIncludePicker()->AddMapping(include_name_as_written, map_to);
ERRSYM(includer) << "Adding pragma-export mapping: "
<< include_name_as_written << " -> "
- << quoted_includer << "\n";
+ << map_to.quoted_include << "\n";
+ // Relative includes can be problematic as map keys, because they are
+ // context-dependent. Convert it to a context-free quoted include
+ // (which may contain the full path to the file), and add that too.
+ string map_from = ConvertToQuotedInclude(GetFilePath(includee));
+ if (map_from != include_name_as_written) {
+ MutableGlobalIncludePicker()->AddMapping(map_from, map_to);
+ ERRSYM(includer) << "Adding pragma-export mapping: "
+ << map_from << " -> " << map_to.quoted_include
+ << "\n";
+ }
// We also always keep #includes of .c files: iwyu doesn't touch those.
// TODO(csilvers): instead of IsHeaderFile, check if the file has
diff --git a/tests/cxx/relative_include_of_double_export-d1.h b/tests/cxx/relative_include_of_double_export-d1.h
new file mode 100644
index 0000000..156dfc5
--- /dev/null
+++ b/tests/cxx/relative_include_of_double_export-d1.h
@@ -0,0 +1,15 @@
+//===--- relative_include_of_double_export-d1.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_RELATIVE_INCLUDE_OF_DOUBLE_EXPORT_D1_H_
+#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_RELATIVE_INCLUDE_OF_DOUBLE_EXPORT_D1_H_
+
+#include "export_private_near.h" // IWYU pragma: export
+
+#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_RELATIVE_INCLUDE_OF_DOUBLE_EXPORT_D1_H_
diff --git a/tests/cxx/relative_include_of_double_export.cc b/tests/cxx/relative_include_of_double_export.cc
new file mode 100644
index 0000000..6a1abdf
--- /dev/null
+++ b/tests/cxx/relative_include_of_double_export.cc
@@ -0,0 +1,23 @@
+//===--- relative_include_of_double_export.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.
+//
+//===----------------------------------------------------------------------===//
+
+// The purpose of this test is to ensure that the following relative include
+// (i.e. using "" to include something at a location relative to this file)
+// remains a relative include rather than being replaced by a different path.
+#include "relative_include_of_double_export-d1.h"
+
+// This class is defined two layers deep within an export double header
+// included via the above.
+PrivateClass x;
+
+/**** IWYU_SUMMARY
+
+(tests/cxx/relative_include_of_double_export.cc has correct #includes/fwd-decls)
+
+***** IWYU_SUMMARY */