diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2022-12-02 15:34:53 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2022-12-02 15:38:09 +0200 |
commit | 649d388ff422a9a049e2b50768db357a73ee59d5 (patch) | |
tree | 96d7acb53c56cf589d611f41aa15edd7a3cca4a0 /libbuild2/parser.cxx | |
parent | af55babfc0c01abbd0a074b0d2ed86598d6bf628 (diff) |
Fail if scope or target qualification in variable expansion is unknown
There are three options here: we can "fall through" to an outer scope (there
is always the global scope backstop; this is the old semantics, sort of), we
can return NULL straight away, or we can fail. It feels like in most cases
unknown scope or target is a mistake and doing anything other than failing is
just making things harder to debug.
Diffstat (limited to 'libbuild2/parser.cxx')
-rw-r--r-- | libbuild2/parser.cxx | 98 |
1 files changed, 69 insertions, 29 deletions
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index 29fe23f..13a3b12 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -59,27 +59,7 @@ namespace build2 enter_scope (parser& p, dir_path&& d) : p_ (&p), r_ (p.root_), s_ (p.scope_), b_ (p.pbase_) { - // Try hard not to call normalize(). Most of the time we will go just - // one level deeper. - // - bool n (true); - - if (d.relative ()) - { - // Relative scopes are opened relative to out, not src. - // - if (d.simple () && !d.current () && !d.parent ()) - { - d = dir_path (p.scope_->out_path ()) /= d.string (); - n = false; - } - else - d = p.scope_->out_path () / d; - } - - if (n) - d.normalize (); - + complete_normalize (*p.scope_, d); e_ = p.switch_scope (d); } @@ -123,6 +103,31 @@ namespace build2 enter_scope (const enter_scope&) = delete; enter_scope& operator= (const enter_scope&) = delete; + static void + complete_normalize (scope& s, dir_path& d) + { + // Try hard not to call normalize(). Most of the time we will go just + // one level deeper. + // + bool n (true); + + if (d.relative ()) + { + // Relative scopes are opened relative to out, not src. + // + if (d.simple () && !d.current () && !d.parent ()) + { + d = dir_path (s.out_path ()) /= d.string (); + n = false; + } + else + d = s.out_path () / d; + } + + if (n) + d.normalize (); + } + private: parser* p_; scope* r_; @@ -8318,6 +8323,9 @@ namespace build2 lookup parser:: lookup_variable (names&& qual, string&& name, const location& loc) { + // Note that this function can be called during execute (for example, from + // scripts). In particular, this means we cannot use enter_{scope,target}. + if (pre_parse_) return lookup (); @@ -8329,9 +8337,6 @@ namespace build2 // If we are qualified, it can be a scope or a target. // - enter_scope sg; - enter_target tg; - if (qual.empty ()) { s = scope_; @@ -8340,13 +8345,29 @@ namespace build2 } else { + // What should we do if we cannot find the qualification (scope or + // target)? We can "fall through" to an outer scope (there is always the + // global scope backstop), we can return NULL straight away, or we can + // fail. It feels like in most cases unknown scope or target is a + // mistake and doing anything other than failing is just making things + // harder to debug. + // switch (qual.front ().pair) { case '/': { assert (qual.front ().directory ()); - sg = enter_scope (*this, move (qual.front ().dir)); - s = scope_; + + dir_path& d (qual.front ().dir); + enter_scope::complete_normalize (*scope_, d); + + s = &ctx->scopes.find_out (d); + + if (s->out_path () != d) + fail (loc) << "unknown scope " << d << " in scope-qualified " + << "variable " << name << " expansion" << + info << "did you forget to include the corresponding buildfile?"; + break; } default: @@ -8356,8 +8377,24 @@ namespace build2 if (n.pair) o = move (qual.back ()); - tg = enter_target (*this, move (n), move (o), true, loc, trace); - t = target_; + t = enter_target::find_target (*this, n, o, loc, trace); + + if (t == nullptr || !operator>= (t->decl, target_decl::implied)) // VC14 + { + diag_record dr (fail (loc)); + + dr << "unknown target " << n; + + if (n.pair && !o.dir.empty ()) + dr << '@' << o.dir; + + dr << " in target-qualified variable " << name << " expansion"; + } + + // Use the target's var_pool for good measure. + // + s = &t->base_scope (); + break; } } @@ -8365,10 +8402,13 @@ namespace build2 // Lookup. // - if (const variable* pvar = scope_->var_pool ().find (name)) + if (const variable* pvar = + (s != nullptr ? s : scope_)->var_pool ().find (name)) { auto& var (*pvar); + // Note: the order of the following blocks is important. + if (p != nullptr) { // The lookup depth is a bit of a hack but should be harmless since |