aboutsummaryrefslogtreecommitdiff
path: root/src/libexpr
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexpr')
-rw-r--r--src/libexpr/attr-path.cc10
-rw-r--r--src/libexpr/eval-cache.cc35
-rw-r--r--src/libexpr/eval-cache.hh8
-rw-r--r--src/libexpr/eval.cc59
-rw-r--r--src/libexpr/value.hh4
5 files changed, 77 insertions, 39 deletions
diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc
index eb0e706c7..32deecfae 100644
--- a/src/libexpr/attr-path.cc
+++ b/src/libexpr/attr-path.cc
@@ -74,8 +74,14 @@ std::pair<Value *, Pos> findAlongAttrPath(EvalState & state, const std::string &
throw Error("empty attribute name in selection path '%1%'", attrPath);
Bindings::iterator a = v->attrs->find(state.symbols.create(attr));
- if (a == v->attrs->end())
- throw AttrPathNotFound("attribute '%1%' in selection path '%2%' not found", attr, attrPath);
+ if (a == v->attrs->end()) {
+ std::set<std::string> attrNames;
+ for (auto & attr : *v->attrs)
+ attrNames.insert(attr.name);
+
+ auto suggestions = Suggestions::bestMatches(attrNames, attr);
+ throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath);
+ }
v = &*a->value;
pos = *a->pos;
}
diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc
index 00d0749f9..188223957 100644
--- a/src/libexpr/eval-cache.cc
+++ b/src/libexpr/eval-cache.cc
@@ -406,6 +406,16 @@ Value & AttrCursor::forceValue()
return v;
}
+Suggestions AttrCursor::getSuggestionsForAttr(Symbol name)
+{
+ auto attrNames = getAttrs();
+ std::set<std::string> strAttrNames;
+ for (auto & name : attrNames)
+ strAttrNames.insert(std::string(name));
+
+ return Suggestions::bestMatches(strAttrNames, name);
+}
+
std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErrors)
{
if (root->db) {
@@ -446,6 +456,11 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
return nullptr;
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
+ for (auto & attr : *v.attrs) {
+ if (root->db)
+ root->db->setPlaceholder({cachedValue->first, attr.name});
+ }
+
auto attr = v.attrs->get(name);
if (!attr) {
@@ -464,7 +479,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(Symbol name, bool forceErro
cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), placeholder_t()};
}
- return std::make_shared<AttrCursor>(
+ return make_ref<AttrCursor>(
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
}
@@ -473,27 +488,31 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(std::string_view name)
return maybeGetAttr(root->state.symbols.create(name));
}
-std::shared_ptr<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
+ref<AttrCursor> AttrCursor::getAttr(Symbol name, bool forceErrors)
{
auto p = maybeGetAttr(name, forceErrors);
if (!p)
throw Error("attribute '%s' does not exist", getAttrPathStr(name));
- return p;
+ return ref(p);
}
-std::shared_ptr<AttrCursor> AttrCursor::getAttr(std::string_view name)
+ref<AttrCursor> AttrCursor::getAttr(std::string_view name)
{
return getAttr(root->state.symbols.create(name));
}
-std::shared_ptr<AttrCursor> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
+OrSuggestions<ref<AttrCursor>> AttrCursor::findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force)
{
auto res = shared_from_this();
for (auto & attr : attrPath) {
- res = res->maybeGetAttr(attr, force);
- if (!res) return {};
+ auto child = res->maybeGetAttr(attr, force);
+ if (!child) {
+ auto suggestions = res->getSuggestionsForAttr(attr);
+ return OrSuggestions<ref<AttrCursor>>::failed(suggestions);
+ }
+ res = child;
}
- return res;
+ return ref(res);
}
std::string AttrCursor::getString()
diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh
index 43b34ebcb..40f1d4ffc 100644
--- a/src/libexpr/eval-cache.hh
+++ b/src/libexpr/eval-cache.hh
@@ -94,15 +94,17 @@ public:
std::string getAttrPathStr(Symbol name) const;
+ Suggestions getSuggestionsForAttr(Symbol name);
+
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name, bool forceErrors = false);
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name);
- std::shared_ptr<AttrCursor> getAttr(Symbol name, bool forceErrors = false);
+ ref<AttrCursor> getAttr(Symbol name, bool forceErrors = false);
- std::shared_ptr<AttrCursor> getAttr(std::string_view name);
+ ref<AttrCursor> getAttr(std::string_view name);
- std::shared_ptr<AttrCursor> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
+ OrSuggestions<ref<AttrCursor>> findAlongAttrPath(const std::vector<Symbol> & attrPath, bool force = false);
std::string getString();
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 193358161..5bf161cc0 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -63,9 +63,15 @@ static char * dupString(const char * s)
}
-static char * dupStringWithLen(const char * s, size_t size)
+// When there's no need to write to the string, we can optimize away empty
+// string allocations.
+// This function handles makeImmutableStringWithLen(null, 0) by returning the
+// empty string.
+static const char * makeImmutableStringWithLen(const char * s, size_t size)
{
char * t;
+ if (size == 0)
+ return "";
#if HAVE_BOEHMGC
t = GC_STRNDUP(s, size);
#else
@@ -75,6 +81,10 @@ static char * dupStringWithLen(const char * s, size_t size)
return t;
}
+static inline const char * makeImmutableString(std::string_view s) {
+ return makeImmutableStringWithLen(s.data(), s.size());
+}
+
RootValue allocRootValue(Value * v)
{
@@ -86,15 +96,10 @@ RootValue allocRootValue(Value * v)
}
-void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v)
+void printValue(std::ostream & str, std::set<const void *> & seen, const Value & v)
{
checkInterrupt();
- if (!active.insert(&v).second) {
- str << "<CYCLE>";
- return;
- }
-
switch (v.internalType) {
case tInt:
str << v.integer;
@@ -120,24 +125,32 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
str << "null";
break;
case tAttrs: {
- str << "{ ";
- for (auto & i : v.attrs->lexicographicOrder()) {
- str << i->name << " = ";
- printValue(str, active, *i->value);
- str << "; ";
+ if (!v.attrs->empty() && !seen.insert(v.attrs).second)
+ str << "<REPEAT>";
+ else {
+ str << "{ ";
+ for (auto & i : v.attrs->lexicographicOrder()) {
+ str << i->name << " = ";
+ printValue(str, seen, *i->value);
+ str << "; ";
+ }
+ str << "}";
}
- str << "}";
break;
}
case tList1:
case tList2:
case tListN:
- str << "[ ";
- for (auto v2 : v.listItems()) {
- printValue(str, active, *v2);
- str << " ";
+ if (v.listSize() && !seen.insert(v.listElems()).second)
+ str << "<REPEAT>";
+ else {
+ str << "[ ";
+ for (auto v2 : v.listItems()) {
+ printValue(str, seen, *v2);
+ str << " ";
+ }
+ str << "]";
}
- str << "]";
break;
case tThunk:
case tApp:
@@ -161,15 +174,13 @@ void printValue(std::ostream & str, std::set<const Value *> & active, const Valu
default:
abort();
}
-
- active.erase(&v);
}
std::ostream & operator << (std::ostream & str, const Value & v)
{
- std::set<const Value *> active;
- printValue(str, active, v);
+ std::set<const void *> seen;
+ printValue(str, seen, v);
return str;
}
@@ -804,7 +815,7 @@ LocalNoInline(void addErrorTrace(Error & e, const Pos & pos, const char * s, con
void Value::mkString(std::string_view s)
{
- mkString(dupStringWithLen(s.data(), s.size()));
+ mkString(makeImmutableString(s));
}
@@ -835,7 +846,7 @@ void Value::mkStringMove(const char * s, const PathSet & context)
void Value::mkPath(std::string_view s)
{
- mkPath(dupStringWithLen(s.data(), s.size()));
+ mkPath(makeImmutableString(s));
}
diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh
index 3fdff71a5..d0fa93e92 100644
--- a/src/libexpr/value.hh
+++ b/src/libexpr/value.hh
@@ -114,8 +114,8 @@ struct Value
private:
InternalType internalType;
-friend std::string showType(const Value & v);
-friend void printValue(std::ostream & str, std::set<const Value *> & active, const Value & v);
+ friend std::string showType(const Value & v);
+ friend void printValue(std::ostream & str, std::set<const void *> & seen, const Value & v);
public: