aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2021-01-12 10:28:29 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2021-01-12 10:28:29 +0200
commite21f9ae1a3e36160259a449e06ff52692e58b28c (patch)
tree6bbc40255704212d5fabba94afba6d02157657e6 /libbuild2
parent3740ef0a57116e35445379b8cc31868718729889 (diff)
Diagnose typed and project-qualified empty names
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/file.cxx5
-rw-r--r--libbuild2/name.hxx4
-rw-r--r--libbuild2/parser.cxx91
3 files changed, 70 insertions, 30 deletions
diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx
index 396b0fe..5d1487f 100644
--- a/libbuild2/file.cxx
+++ b/libbuild2/file.cxx
@@ -2549,6 +2549,11 @@ namespace build2
context& ctx (base.ctx);
assert (ctx.phase == run_phase::load);
+ // Validate the name.
+ //
+ if (tgt.qualified () && tgt.empty ())
+ fail (loc) << "project-qualified empty name " << tgt;
+
// If metadata is requested, delegate to import_direct() which will lookup
// the target and verify the metadata was loaded.
//
diff --git a/libbuild2/name.hxx b/libbuild2/name.hxx
index b25a3e3..8385e7c 100644
--- a/libbuild2/name.hxx
+++ b/libbuild2/name.hxx
@@ -27,8 +27,8 @@ namespace build2
// empty, then it means the name is in a project other than our own (e.g.,
// it is installed).
//
- // A type or project can only be specified if either directory or value are
- // not empty.
+ // A type can only be specified if either directory or value are not empty.
+ // We allow project-qualified empty names for reversibility.
//
// If pair is not '\0', then this name and the next in the list form a
// pair. Can be used as a bool flag.
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index 8118e0e..ab4fa48 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -2488,7 +2488,7 @@ namespace build2
// General import format:
//
- // import[?!] [<attrs>] [<var>=](<project>|<project>%<target>])+
+ // import[?!] [<attrs>] [<var>=](<target>|<project>%<target>])+
//
bool opt (t.value.back () == '?');
bool ph2 (opt || t.value.back () == '!');
@@ -4520,6 +4520,22 @@ namespace build2
return make_pair (has, l);
}
+ // Add a name verifying it is valid.
+ //
+ static inline name&
+ append_name (names& ns,
+ optional<project_name> p, dir_path d, string t, string v,
+ const location& loc)
+ {
+ // The directory/value must not be empty if we have a type.
+ //
+ if (d.empty () && v.empty () && !t.empty ())
+ fail (loc) << "typed empty name";
+
+ ns.emplace_back (move (p), move (d), move (t), move (v));
+ return ns.back ();
+ }
+
// Splice names from the name view into the destination name list while
// doing sensible things with pairs, types, etc. Return the number of
// the names added.
@@ -4623,8 +4639,8 @@ namespace build2
ns.push_back (ns[pairn - 1]);
}
- ns.emplace_back (move (p), move (d), move (t), move (v));
- ns.back ().pair = cn.pair;
+ name& r (append_name (ns, move (p), move (d), move (t), move (v), loc));
+ r.pair = cn.pair;
}
return ns.size () - start;
@@ -4694,6 +4710,9 @@ namespace build2
//
auto append = [&r, &dir] (string&& v, optional<string>&& e, bool a)
{
+ // Here we can assume either dir or value are not empty (comes from
+ // pattern expansion).
+ //
name n (dir ? name (dir_path (move (v))) : name (move (v)));
if (a)
@@ -5465,6 +5484,10 @@ namespace build2
}
else
{
+ // This is either a simple name (untyped concatenation; in which
+ // case it is always valid) or it came from type concatenation in
+ // which case we can assume the result is valid.
+ //
ns.push_back (move (concat_data));
// Clear the type information if that's not the only name.
@@ -5815,18 +5838,22 @@ namespace build2
if (dp != nullptr)
dir = *dp / dir;
- ns.emplace_back (*pp1,
- move (dir),
- (tp != nullptr ? *tp : string ()),
- string ());
+ append_name (
+ ns,
+ *pp1, move (dir), (tp != nullptr ? *tp : string ()), string (),
+ loc);
+
continue;
}
}
- ns.emplace_back (*pp1,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- move (val));
+ append_name (ns,
+ *pp1,
+ (dp != nullptr ? *dp : dir_path ()),
+ (tp != nullptr ? *tp : string ()),
+ move (val),
+ loc);
+
continue;
}
@@ -6329,10 +6356,12 @@ namespace build2
// Empty LHS, (e.g., @y), create an empty name. The second test
// will be in effect if we have something like v=@y.
//
- ns.emplace_back (pp,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- string ());
+ append_name (ns,
+ pp,
+ (dp != nullptr ? *dp : dir_path ()),
+ (tp != nullptr ? *tp : string ()),
+ string (),
+ get_location (t));
count = 1;
}
else if (count > 1)
@@ -6347,10 +6376,12 @@ namespace build2
//
if (peeked ().separated)
{
- ns.emplace_back (pp,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- string ());
+ append_name (ns,
+ pp,
+ (dp != nullptr ? *dp : dir_path ()),
+ (tp != nullptr ? *tp : string ()),
+ string (),
+ get_location (t));
count = 0;
}
}
@@ -6364,7 +6395,7 @@ namespace build2
if (!first)
break;
- if (tt == type::rcbrace) // Empty name, e.g., dir{}.
+ if (tt == type::rcbrace) // Empty name, e.g., {}.
{
// If we are a second half of a pair, add another first half
// unless this is the first instance.
@@ -6372,10 +6403,12 @@ namespace build2
if (pairn != 0 && pairn != ns.size ())
ns.push_back (ns[pairn - 1]);
- ns.emplace_back (pp,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- string ());
+ append_name (ns,
+ pp,
+ (dp != nullptr ? *dp : dir_path ()),
+ (tp != nullptr ? *tp : string ()),
+ string (),
+ get_location (t));
break;
}
else
@@ -6388,10 +6421,12 @@ namespace build2
//
if (!ns.empty () && ns.back ().pair)
{
- ns.emplace_back (pp,
- (dp != nullptr ? *dp : dir_path ()),
- (tp != nullptr ? *tp : string ()),
- string ());
+ append_name (ns,
+ pp,
+ (dp != nullptr ? *dp : dir_path ()),
+ (tp != nullptr ? *tp : string ()),
+ string (),
+ get_location (t));
}
if (pre_parse_)