From 05ee8c20d83c2f108aa71a65e19b7adff8ff9aa0 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 11 Jan 2024 14:45:15 +0200 Subject: Fail with unable to import rather than unknown target type --- libbuild2/file.cxx | 69 +++++++++++++++++++++++++++++++++++++++----- libbuild2/file.hxx | 11 +++++++ libbuild2/functions-name.cxx | 34 ++++++++++++++++++---- libbuild2/parser.cxx | 21 ++++++++++++-- libbuild2/scope.cxx | 12 +++++--- libbuild2/scope.hxx | 12 ++++++-- libbuild2/scope.ixx | 6 ++-- 7 files changed, 140 insertions(+), 25 deletions(-) diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx index e62e607..81a9dac 100644 --- a/libbuild2/file.cxx +++ b/libbuild2/file.cxx @@ -2063,16 +2063,13 @@ namespace build2 &t); } - // Suggest appropriate ways to import the specified target (as type and - // name) from the specified project. - // - static void + void import_suggest (const diag_record& dr, const project_name& pn, const target_type* tt, const string& tn, bool rule_hint, - const char* qual = nullptr) + const char* qual) { string pv (pn.variable ()); @@ -2899,6 +2896,11 @@ namespace build2 static names import2_buildfile (context&, names&&, bool, const location&); + static const target* + import2 (context&, const scope&, names&, + const string&, bool, const optional&, bool, + const location&); + pair import (scope& base, name tgt, @@ -2971,7 +2973,7 @@ namespace build2 // fallback case. // if (const target* t = import2 (ctx, - base.find_prerequisite_key (ns, loc), + base, ns, *ph2, opt && !r.second /* optional */, nullopt /* metadata */, @@ -3168,6 +3170,8 @@ namespace build2 return *t; } + // NOTE: see similar code in import2() below if changing anything here. + if (opt || exist) return nullptr; @@ -3185,6 +3189,57 @@ namespace build2 dr << endf; } + // As above but with scope/ns instead of pk. This version deals with the + // unknown target type case. + // + static const target* + import2 (context& ctx, + const scope& base, names& ns, + const string& hint, + bool opt, + const optional& meta, + bool exist, + const location& loc) + { + // If we have a rule hint, then it's natural to expect this target type is + // known to the importing project. Ditto for project-less import. + // + const target_type* tt (nullptr); + if (hint.empty ()) + { + size_t n; + if ((n = ns.size ()) != 0 && n == (ns[0].pair ? 2 : 1)) + { + const name& n (ns.front ()); + + if (n.typed () && !n.proj->empty ()) + { + tt = base.find_target_type (n.type); + + if (tt == nullptr) + { + // A subset of code in the above version of import2(). + // + if (opt || exist) + return nullptr; + + diag_record dr; + dr << fail (loc) << "unable to import target " << ns; + import_suggest (dr, *n.proj, nullptr, string (), meta.has_value ()); + } + } + } + } + + return import2 (ctx, + base.find_prerequisite_key (ns, loc, tt), + hint, + opt, + meta, + exist, + loc); + } + static names import2_buildfile (context&, names&& ns, bool opt, const location& loc) { @@ -3323,7 +3378,7 @@ namespace build2 // fallback case. // pt = import2 (ctx, - base.find_prerequisite_key (ns, loc), + base, ns, *ph2, opt && !r.second, meta, diff --git a/libbuild2/file.hxx b/libbuild2/file.hxx index 6c5097d..68c284b 100644 --- a/libbuild2/file.hxx +++ b/libbuild2/file.hxx @@ -518,6 +518,17 @@ namespace build2 bool metadata, const location&); + // Suggest appropriate ways to import the specified target (as type and + // name) from the specified project. + // + void + import_suggest (const diag_record&, + const project_name&, + const target_type*, + const string& name, + bool rule_hint, + const char* qual = nullptr); + // Create a build system project in the specified directory. // LIBBUILD2_SYMEXPORT void diff --git a/libbuild2/functions-name.cxx b/libbuild2/functions-name.cxx index fcb492f..456f85b 100644 --- a/libbuild2/functions-name.cxx +++ b/libbuild2/functions-name.cxx @@ -97,7 +97,18 @@ namespace build2 const target_type* ntt (to_target_type (s, n, o).first); if (ntt == nullptr) + { + // If this is an imported target and the target type is unknown, then + // it cannot possibly match one of the known types. We handle it like + // this instead of failing because the later failure (e.g., as a + // result of this target listed as prerequisite) will have more + // accurate diagnostics. See also filter() below. + // + if (n.proj) + return false; + fail << "unknown target type " << n.type << " in " << n; + } return ntt->is_a (*tt); } @@ -142,13 +153,24 @@ namespace build2 const target_type* ntt (to_target_type (s, c, p ? *++i : name ()).first); if (ntt == nullptr) - fail << "unknown target type " << n.type << " in " << n; + { + // If this is an imported target and the target type is unknown, then + // it cannot possibly match one of the known types. We handle it like + // this instead of failing because the later failure (e.g., as a + // result of this target listed as prerequisite) will have more + // accurate diagnostics. See also is_a() above. + // + if (!n.proj) + fail << "unknown target type " << n.type << " in " << n; + } - if ((find_if (tts.begin (), tts.end (), - [ntt] (const target_type* tt) - { - return ntt->is_a (*tt); - }) != tts.end ()) != out) + if (ntt != nullptr + ? (find_if (tts.begin (), tts.end (), + [ntt] (const target_type* tt) + { + return ntt->is_a (*tt); + }) != tts.end ()) != out + : out) { r.push_back (move (n)); if (p) diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 71465f0..baf404a 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -2778,9 +2778,24 @@ namespace build2 optional& e (rp.second); if (t == nullptr) - fail (ploc) << "unknown target type " << n.type << - info << "perhaps the module that defines this target type is " - << "not loaded by project " << *scope_->root_scope (); + { + if (n.proj) + { + // If the target type is unknown then no phase 2 import (like + // rule-specific search) can possibly succeed so we can fail now and + // with a more accurate reason. See import2(names) for background. + // + diag_record dr; + dr << fail (ploc) << "unable to import target " << n; + import_suggest (dr, *n.proj, nullptr, string (), false); + } + else + { + fail (ploc) << "unknown target type " << n.type << + info << "perhaps the module that defines this target type is " + << "not loaded by project " << *scope_->root_scope (); + } + } if (t->factory == nullptr) fail (ploc) << "abstract target type " << t->name << "{}"; diff --git a/libbuild2/scope.cxx b/libbuild2/scope.cxx index c1c5ed7..4c9f301 100644 --- a/libbuild2/scope.cxx +++ b/libbuild2/scope.cxx @@ -893,9 +893,11 @@ namespace build2 } pair> scope:: - find_prerequisite_type (name& n, name& o, const location& loc) const + find_prerequisite_type (name& n, name& o, + const location& loc, + const target_type* tt) const { - auto r (find_target_type (n, loc)); + auto r (find_target_type (n, loc, tt)); if (r.first == nullptr) fail (loc) << "unknown target type " << n.type << " in " << n; @@ -919,14 +921,16 @@ namespace build2 } prerequisite_key scope:: - find_prerequisite_key (names& ns, const location& loc) const + find_prerequisite_key (names& ns, + const location& loc, + const target_type* tt) const { if (size_t n = ns.size ()) { if (n == (ns[0].pair ? 2 : 1)) { name dummy; - return find_prerequisite_key (ns[0], n == 1 ? dummy : ns[1], loc); + return find_prerequisite_key (ns[0], n == 1 ? dummy : ns[1], loc, tt); } } diff --git a/libbuild2/scope.hxx b/libbuild2/scope.hxx index 50347b2..3f6b0af 100644 --- a/libbuild2/scope.hxx +++ b/libbuild2/scope.hxx @@ -376,15 +376,21 @@ namespace build2 // directories. // pair> - find_prerequisite_type (name&, name&, const location&) const; + find_prerequisite_type (name&, name&, + const location&, + const target_type* tt = nullptr) const; // As above, but return a prerequisite key. // prerequisite_key - find_prerequisite_key (name&, name&, const location&) const; + find_prerequisite_key (name&, name&, + const location&, + const target_type* = nullptr) const; prerequisite_key - find_prerequisite_key (names&, const location&) const; + find_prerequisite_key (names&, + const location&, + const target_type* = nullptr) const; // Dynamically derive a new target type from an existing one. Return the // reference to the target type and an indicator of whether it was diff --git a/libbuild2/scope.ixx b/libbuild2/scope.ixx index 5d76a7f..4543f2c 100644 --- a/libbuild2/scope.ixx +++ b/libbuild2/scope.ixx @@ -158,9 +158,11 @@ namespace build2 } inline prerequisite_key scope:: - find_prerequisite_key (name& n, name& o, const location& loc) const + find_prerequisite_key (name& n, name& o, + const location& loc, + const target_type* tt) const { - auto p (find_prerequisite_type (n, o, loc)); + auto p (find_prerequisite_type (n, o, loc, tt)); return prerequisite_key { n.proj, { -- cgit v1.1