summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Bytheway <jbytheway@gmail.com>2019-04-12 21:52:18 +0100
committerKim Gräsman <kim.grasman@gmail.com>2019-09-01 16:41:53 +0200
commit151371887144b9140b2541fd381562f8ae1d977e (patch)
tree6b7c457fa6cf58756cc93c3be3fc8f2cdb079048
parentfe8e57b44123e782b1f69fae8216eb8aab51e05f (diff)
Fix combination of pragma export and "" includes
Fix some corner cases that can arise due to interaction between pragma export and includes that are relative to the including header (i.e. includes that can only be done using "", not <>). Because headers are mostly handled as quoted includes for the purposes of mapping, this can go wrong with relative includes because they are not always relative to the same place, and at some places in the code we don't even know what they're relative to. Fix this by tracking full paths in parallel or instead of quoted includes in certain places.
-rw-r--r--iwyu_include_picker.cc21
-rw-r--r--iwyu_include_picker.h9
-rw-r--r--iwyu_output.cc4
-rw-r--r--iwyu_preprocessor.cc13
-rw-r--r--tests/cxx/export_near.h20
-rw-r--r--tests/cxx/relative_include_of_export.cc21
6 files changed, 69 insertions, 19 deletions
diff --git a/iwyu_include_picker.cc b/iwyu_include_picker.cc
index edc3c9e..d950d9b 100644
--- a/iwyu_include_picker.cc
+++ b/iwyu_include_picker.cc
@@ -1037,8 +1037,9 @@ string FindFileInSearchPath(const vector<string>& search_path,
} // anonymous namespace
-MappedInclude::MappedInclude(const string& q)
+MappedInclude::MappedInclude(const string& q, const string& p)
: quoted_include(q)
+ , path(p)
{
CHECK_(IsQuotedInclude(quoted_include)) <<
"Must be quoted include, was: " << quoted_include;
@@ -1094,7 +1095,7 @@ void IncludePicker::AddDirectInclude(const string& includer_filepath,
// to our map, but harmless.
const string quoted_includer = ConvertToQuotedInclude(includer_filepath);
const string quoted_includee = ConvertToQuotedInclude(includee_filepath);
- MappedInclude mapped_includer(quoted_includer);
+ MappedInclude mapped_includer(quoted_includer, includer_filepath);
quoted_includes_to_quoted_includers_[quoted_includee].insert(quoted_includer);
const pair<string, string> key(includer_filepath, includee_filepath);
@@ -1339,7 +1340,7 @@ vector<MappedInclude> IncludePicker::GetCandidateHeadersForFilepath(
GetPublicValues(filepath_include_map_, quoted_header);
if (retval.empty()) {
// the filepath isn't in include_map, so just quote and return it.
- retval.push_back(MappedInclude(quoted_header));
+ retval.push_back(MappedInclude(quoted_header, filepath));
}
return retval;
}
@@ -1361,7 +1362,8 @@ vector<string> IncludePicker::GetCandidateHeadersForFilepathIncludedFrom(
FindInMap(&friend_to_headers_map_, quoted_includer);
if (headers_with_includer_as_friend != nullptr &&
ContainsKey(*headers_with_includer_as_friend, included_filepath)) {
- mapped_includes.push_back(MappedInclude(quoted_includee));
+ mapped_includes.push_back(
+ MappedInclude(quoted_includee, including_filepath));
} else {
mapped_includes =
GetCandidateHeadersForFilepath(included_filepath, including_filepath);
@@ -1382,15 +1384,14 @@ vector<string> IncludePicker::GetCandidateHeadersForFilepathIncludedFrom(
// ConvertToQuotedInclude because it avoids trouble when the same
// file is accessible via different include search-paths, or is
// accessed via a symlink.
- const string& quoted_include_as_written =
- MaybeGetIncludeNameAsWritten(including_filepath, included_filepath);
vector<string> retval;
for (MappedInclude& mapped_include : mapped_includes) {
- if (!quoted_include_as_written.empty() &&
- mapped_include.quoted_include == quoted_includee) {
- retval.push_back(quoted_include_as_written);
- } else {
+ const string& quoted_include_as_written =
+ MaybeGetIncludeNameAsWritten(including_filepath, mapped_include.path);
+ if (quoted_include_as_written.empty()) {
retval.push_back(mapped_include.quoted_include);
+ } else {
+ retval.push_back(quoted_include_as_written);
}
}
return retval;
diff --git a/iwyu_include_picker.h b/iwyu_include_picker.h
index 872d8d2..49f02ab 100644
--- a/iwyu_include_picker.h
+++ b/iwyu_include_picker.h
@@ -67,10 +67,15 @@ struct IncludeMapEntry;
enum IncludeVisibility { kUnusedVisibility, kPublic, kPrivate };
+// When a symbol or file is mapped to an include, that include is represented
+// by this struct. It always has a quoted_include and may also have a path
+// (depending on its origin).
struct MappedInclude {
- explicit MappedInclude(const string& quoted_include);
+ explicit MappedInclude(const string& quoted_include,
+ const string& path = {});
string quoted_include;
+ string path;
};
class IncludePicker {
@@ -140,6 +145,8 @@ class IncludePicker {
// not when #included from "qux/quux.h". In the common case there's
// no special-casing, and this falls back on
// GetCandidateHeadersForFilepath().
+ // Furthermore, knowing the including file allows use to convert each
+ // MappedInclude in the result to a simple string (quoted include).
vector<string> GetCandidateHeadersForFilepathIncludedFrom(
const string& included_filepath, const string& including_filepath) const;
diff --git a/iwyu_output.cc b/iwyu_output.cc
index 3dd54ad..3337df3 100644
--- a/iwyu_output.cc
+++ b/iwyu_output.cc
@@ -2083,8 +2083,8 @@ void IwyuFileInfo::HandlePreprocessingDone() {
ERRSYM(file_) << "Mark " << quoted_file_
<< " as public header for " << private_include
<< " because used macro is defined by includer.\n";
- MutableGlobalIncludePicker()->AddMapping(private_include,
- MappedInclude(quoted_file_));
+ MutableGlobalIncludePicker()->AddMapping(
+ private_include, MappedInclude(quoted_file_, GetFilePath(file_)));
MutableGlobalIncludePicker()->MarkIncludeAsPrivate(private_include);
}
}
diff --git a/iwyu_preprocessor.cc b/iwyu_preprocessor.cc
index e132abc..a70e420 100644
--- a/iwyu_preprocessor.cc
+++ b/iwyu_preprocessor.cc
@@ -411,13 +411,14 @@ void IwyuPreprocessorInfo::MaybeProtectInclude(
LineHasText(includer_loc, "/* IWYU pragma: export") ||
HasOpenBeginExports(includer)) {
protect_reason = "pragma_export";
- const string quoted_includer =
- ConvertToQuotedInclude(GetFilePath(includer));
- MutableGlobalIncludePicker()->AddMapping(include_name_as_written,
- MappedInclude(quoted_includer));
+ const string includer_path = GetFilePath(includer);
+ const string quoted_includer = ConvertToQuotedInclude(includer_path);
+ MutableGlobalIncludePicker()->AddMapping(
+ include_name_as_written,
+ MappedInclude(quoted_includer, includer_path));
ERRSYM(includer) << "Adding pragma-export mapping: "
- << include_name_as_written << " -> " << quoted_includer
- << "\n";
+ << include_name_as_written << " -> "
+ << quoted_includer << "\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/export_near.h b/tests/cxx/export_near.h
new file mode 100644
index 0000000..15fef16
--- /dev/null
+++ b/tests/cxx/export_near.h
@@ -0,0 +1,20 @@
+//===--- export_near.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.
+//
+//===----------------------------------------------------------------------===//
+
+// This file is meant to be a generic exporting header. Similarly to
+// direct_near.h, it includes indirect.h. However, unlike that file, this one
+// exports indirect.h, and therefore is a valid file to include to access
+// IndirectClass.
+
+#ifndef INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPORT_NEAR_H_
+#define INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPORT_NEAR_H_
+
+#include "indirect.h" // IWYU pragma: export
+
+#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_EXPORT_NEAR_H_
diff --git a/tests/cxx/relative_include_of_export.cc b/tests/cxx/relative_include_of_export.cc
new file mode 100644
index 0000000..4fd76cb
--- /dev/null
+++ b/tests/cxx/relative_include_of_export.cc
@@ -0,0 +1,21 @@
+//===--- relative_include_of_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.
+//
+//===----------------------------------------------------------------------===//
+
+// 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 "export_near.h"
+
+IndirectClass x;
+
+/**** IWYU_SUMMARY
+
+(tests/cxx/relative_include_of_export.cc has correct #includes/fwd-decls)
+
+***** IWYU_SUMMARY */