diff options
author | John Bytheway <jbytheway@gmail.com> | 2019-08-19 15:57:58 -0400 |
---|---|---|
committer | Kim Gräsman <kim.grasman@gmail.com> | 2019-09-01 16:41:53 +0200 |
commit | 7662c814a4b4590057c03a84c3235f0a20a81b22 (patch) | |
tree | a935f655951eff9175a3b5dd26bf03062c700ecd | |
parent | 2bd1ddee4a38152462c78abaa930d5a4c9604ea0 (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.cc | 13 | ||||
-rw-r--r-- | iwyu_preprocessor.cc | 17 | ||||
-rw-r--r-- | tests/cxx/relative_include_of_double_export-d1.h | 15 | ||||
-rw-r--r-- | tests/cxx/relative_include_of_double_export.cc | 23 |
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 */ |