Index: tools/gn/header_checker.cc |
diff --git a/tools/gn/header_checker.cc b/tools/gn/header_checker.cc |
index 54dfb50c8a7e3fe6bbdc889cb6464faa5af5bb77..3b92c8522c32cb3b11184b304907cb791a3546ac 100644 |
--- a/tools/gn/header_checker.cc |
+++ b/tools/gn/header_checker.cc |
@@ -19,6 +19,35 @@ |
#include "tools/gn/target.h" |
#include "tools/gn/trace.h" |
+namespace { |
+ |
+// This class makes InputFiles on the stack as it reads files to check. When |
+// we throw an error, the Err indicates a locatin which has a pointer to |
+// an InputFile that must persist as long as the Err does. |
+// |
+// To make this work, this function creates a clone of the InputFile managed |
+// by the InputFileManager so the error can refer to something that |
+// persists. This means that the current file contents will live as long as |
+// the program, but this is OK since we're erroring out anyway. |
+LocationRange CreatePersistentRange(const InputFile& input_file, |
+ const LocationRange& range) { |
+ InputFile* clone_input_file; |
+ std::vector<Token>* tokens; // Don't care about this. |
+ scoped_ptr<ParseNode>* parse_root; // Don't care about this. |
+ |
+ g_scheduler->input_file_manager()->AddDynamicInput( |
+ input_file.name(), &clone_input_file, &tokens, &parse_root); |
+ clone_input_file->SetContents(input_file.contents()); |
+ |
+ return LocationRange( |
+ Location(clone_input_file, range.begin().line_number(), |
+ range.begin().char_offset()), |
+ Location(clone_input_file, range.end().line_number(), |
+ range.end().char_offset())); |
+} |
+ |
+} // namespace |
+ |
HeaderChecker::HeaderChecker(const BuildSettings* build_settings, |
const std::vector<const Target*>& targets) |
: main_loop_(base::MessageLoop::current()), |
@@ -137,11 +166,15 @@ bool HeaderChecker::CheckFile(const Target* from_target, |
return false; |
} |
- CIncludeIterator iter(contents); |
+ InputFile input_file(file); |
+ input_file.SetContents(contents); |
+ |
+ CIncludeIterator iter(&input_file); |
base::StringPiece current_include; |
- while (iter.GetNextIncludeString(¤t_include)) { |
+ LocationRange range; |
+ while (iter.GetNextIncludeString(¤t_include, &range)) { |
SourceFile include = SourceFileForInclude(current_include); |
- if (!CheckInclude(from_target, file, include, err)) |
+ if (!CheckInclude(from_target, input_file, include, range, err)) |
return false; |
} |
@@ -151,8 +184,9 @@ bool HeaderChecker::CheckFile(const Target* from_target, |
// If the file exists, it must be in a dependency of the given target, and it |
// must be public in that dependency. |
bool HeaderChecker::CheckInclude(const Target* from_target, |
- const SourceFile& source_file, |
+ const InputFile& source_file, |
const SourceFile& include_file, |
+ const LocationRange& range, |
Err* err) const { |
// Assume if the file isn't declared in our sources that we don't need to |
// check it. It would be nice if we could give an error if this happens, but |
@@ -176,17 +210,28 @@ bool HeaderChecker::CheckInclude(const Target* from_target, |
return true; |
if (IsDependencyOf(targets[i].target, from_target)) { |
- // The include is in a target that's a proper dependency. Now verify |
- // that the include is a public file. |
+ // The include is in a target that's a proper dependency. Verify that |
+ // the including target has visibility. |
+ if (!targets[i].target->visibility().CanSeeMe(from_target->label())) { |
+ std::string msg = "The included file is in " + |
+ targets[i].target->label().GetUserVisibleName(false) + |
+ "\nwhich is not visible from " + |
+ from_target->label().GetUserVisibleName(false) + |
+ "\n(see \"gn help visibility\")."; |
+ |
+ // Danger: must call CreatePersistentRange to put in Err. |
+ *err = Err(CreatePersistentRange(source_file, range), |
+ "Including a header from non-visible target.", msg); |
+ return false; |
+ } |
+ |
+ // The file must also be public in the target. |
if (!targets[i].is_public) { |
- // Depending on a private header. |
- std::string msg = "The file " + source_file.value() + |
- "\nincludes " + include_file.value() + |
- "\nwhich is private to the target " + |
- targets[i].target->label().GetUserVisibleName(false); |
- |
- // TODO(brettw) blame the including file. |
- *err = Err(NULL, "Including a private header.", msg); |
+ // Danger: must call CreatePersistentRange to put in Err. |
+ *err = Err(CreatePersistentRange(source_file, range), |
+ "Including a private header.", |
+ "This file is private to the target " + |
+ targets[i].target->label().GetUserVisibleName(false)); |
return false; |
} |
found_dependency = true; |
@@ -194,21 +239,19 @@ bool HeaderChecker::CheckInclude(const Target* from_target, |
} |
if (!found_dependency) { |
- std::string msg = |
- source_file.value() + " includes the header\n" + |
- include_file.value() + " which is not in any dependency of\n" + |
+ std::string msg = "It is not in any dependency of " + |
from_target->label().GetUserVisibleName(false); |
- msg += "\n\nThe include file is in the target(s):\n"; |
+ msg += "\nThe include file is in the target(s):\n"; |
for (size_t i = 0; i < targets.size(); i++) |
msg += " " + targets[i].target->label().GetUserVisibleName(false) + "\n"; |
+ if (targets.size() > 1) |
+ msg += "at least one of "; |
+ msg += "which should somehow be reachable from " + |
+ from_target->label().GetUserVisibleName(false); |
- msg += "\nMake sure one of these is a direct or indirect dependency\n" |
- "of " + from_target->label().GetUserVisibleName(false); |
- |
- // TODO(brettw) blame the including file. |
- // Probably this means making and leaking an input file for it, and also |
- // tracking the locations for each include. |
- *err = Err(NULL, "Include not allowed.", msg); |
+ // Danger: must call CreatePersistentRange to put in Err. |
+ *err = Err(CreatePersistentRange(source_file, range), |
+ "Include not allowed.", msg); |
return false; |
} |