Merge changes I823773f4,If63415b5,Ic5a92c8b,I1f713427,I2a6c9f46, ...

* changes:
  StaticSchema: Remove unused method
  Change struct to a function
  Simplify listInstancePath_class
  Prefer std::variant in dataNode_/schemaNode_
  Get rid of useless variant in get
  Complete slash after lists when inputting "any" path
diff --git a/src/ast_commands.hpp b/src/ast_commands.hpp
index 5ce1cbd..5759eda 100644
--- a/src/ast_commands.hpp
+++ b/src/ast_commands.hpp
@@ -141,7 +141,7 @@
         /> get
         /> get /module:path)";
     bool operator==(const get_& b) const;
-    boost::optional<boost::variant<boost::variant<dataPath_, schemaPath_>, module_>> m_path;
+    boost::optional<boost::variant<dataPath_, schemaPath_, module_>> m_path;
 };
 
 struct describe_ : x3::position_tagged {
diff --git a/src/ast_handlers.hpp b/src/ast_handlers.hpp
index b9cf883..5b5a152 100644
--- a/src/ast_handlers.hpp
+++ b/src/ast_handlers.hpp
@@ -178,14 +178,14 @@
             boost::optional<std::string> module;
             if (ast.m_nodes.back().m_prefix)
                 module = ast.m_nodes.back().m_prefix.value().m_name;
-            container_ cont = boost::get<container_>(ast.m_nodes.back().m_suffix);
+            container_ cont = std::get<container_>(ast.m_nodes.back().m_suffix);
             auto location = pathWithoutLastNode(parserContext.currentSchemaPath());
 
             if (!schema.isPresenceContainer(location, {module, cont.m_name})) {
                 parserContext.m_errorMsg = "This container is not a presence container.";
                 _pass(context) = false;
             }
-        } catch (boost::bad_get&) {
+        } catch (std::bad_variant_access&) {
             parserContext.m_errorMsg = "This is not a container.";
             _pass(context) = false;
         }
@@ -197,13 +197,7 @@
     void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
     {
         auto& parserContext = x3::get<parser_context_tag>(context);
-        if (ast.m_nodes.empty()) {
-            parserContext.m_errorMsg = "This is not a list instance.";
-            _pass(context) = false;
-            return;
-        }
-
-        if (ast.m_nodes.back().m_suffix.type() != typeid(listElement_)) {
+        if (ast.m_nodes.empty() || !std::holds_alternative<listElement_>(ast.m_nodes.back().m_suffix)) {
             parserContext.m_errorMsg = "This is not a list instance.";
             _pass(context) = false;
         }
@@ -215,7 +209,7 @@
     void on_success(Iterator const&, Iterator const&, T& ast, Context const& context)
     {
         auto& parserContext = x3::get<parser_context_tag>(context);
-        if (ast.m_nodes.empty() || ast.m_nodes.back().m_suffix.type() != typeid(leafListElement_)) {
+        if (ast.m_nodes.empty() || !std::holds_alternative<leafListElement_>(ast.m_nodes.back().m_suffix)) {
             parserContext.m_errorMsg = "This is not a leaf list element.";
             _pass(context) = false;
         }
@@ -267,14 +261,14 @@
 
         try {
             auto lastNode = parserContext.currentSchemaPath().m_nodes.back();
-            auto leaf = boost::get<leaf_>(lastNode.m_suffix);
+            auto leaf = std::get<leaf_>(lastNode.m_suffix);
             auto location = pathWithoutLastNode(parserContext.currentSchemaPath());
             ModuleNodePair node{lastNode.m_prefix.flat_map([](const auto& it) { return boost::optional<std::string>{it.m_name}; }), leaf.m_name};
 
             parserContext.m_tmpListKeyLeafPath.m_location = location;
             parserContext.m_tmpListKeyLeafPath.m_node = node;
 
-        } catch (boost::bad_get&) {
+        } catch (std::bad_variant_access&) {
             parserContext.m_errorMsg = "This is not a path to leaf.";
             _pass(context) = false;
         }
@@ -415,13 +409,11 @@
     }
 };
 
-struct commandNamesVisitor {
-    template <typename T>
-    std::string operator()(boost::type<T>)
-    {
-        return T::name;
-    }
-};
+template <typename T>
+std::string commandNamesVisitor (boost::type<T>)
+{
+    return T::name;
+}
 
 struct createCommandSuggestions_class {
     template <typename T, typename Iterator, typename Context>
@@ -432,7 +424,7 @@
 
         parserContext.m_suggestions.clear();
         boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([&parserContext](auto cmd) {
-            parserContext.m_suggestions.insert({commandNamesVisitor()(cmd), " "});
+            parserContext.m_suggestions.insert({commandNamesVisitor(cmd), " "});
         });
     }
 };
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index b9b9b5a..1a26a1c 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -124,8 +124,7 @@
     return this->m_nodes == b.m_nodes;
 }
 
-
-struct nodeToSchemaStringVisitor : public boost::static_visitor<std::string> {
+struct nodeToSchemaStringVisitor {
     std::string operator()(const nodeup_&) const
     {
         return "..";
@@ -148,7 +147,7 @@
     }
 }
 
-struct nodeToDataStringVisitor : public boost::static_visitor<std::string> {
+struct nodeToDataStringVisitor {
     std::string operator()(const listElement_& node) const
     {
         std::ostringstream res;
@@ -176,7 +175,7 @@
 
 std::string nodeToSchemaString(decltype(dataPath_::m_nodes)::value_type node)
 {
-    return boost::apply_visitor(nodeToSchemaStringVisitor(), node.m_suffix);
+    return std::visit(nodeToSchemaStringVisitor(), node.m_suffix);
 }
 
 std::string pathToDataString(const dataPath_& path, Prefixes prefixes)
@@ -188,9 +187,9 @@
 
     for (const auto& it : path.m_nodes) {
         if (it.m_prefix)
-            res = joinPaths(res, it.m_prefix.value().m_name + ":" + boost::apply_visitor(nodeToDataStringVisitor(), it.m_suffix));
+            res = joinPaths(res, it.m_prefix.value().m_name + ":" + std::visit(nodeToDataStringVisitor(), it.m_suffix));
         else
-            res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + boost::apply_visitor(nodeToDataStringVisitor(), it.m_suffix));
+            res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + std::visit(nodeToDataStringVisitor(), it.m_suffix));
     }
 
     return res;
@@ -205,9 +204,9 @@
 
     for (const auto& it : path.m_nodes) {
         if (it.m_prefix)
-            res = joinPaths(res, it.m_prefix.value().m_name + ":" + boost::apply_visitor(nodeToSchemaStringVisitor(), it.m_suffix));
+            res = joinPaths(res, it.m_prefix.value().m_name + ":" + std::visit(nodeToSchemaStringVisitor(), it.m_suffix));
         else
-            res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + boost::apply_visitor(nodeToSchemaStringVisitor(), it.m_suffix));
+            res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + std::visit(nodeToSchemaStringVisitor(), it.m_suffix));
     }
     return res;
 }
@@ -217,19 +216,20 @@
     return pathToSchemaString(dataPathToSchemaPath(path), prefixes);
 }
 
-struct dataSuffixToSchemaSuffix : boost::static_visitor<decltype(schemaNode_::m_suffix)> {
-    auto operator()(const listElement_& listElement) const
+struct dataSuffixToSchemaSuffix {
+    using ReturnType = decltype(schemaNode_::m_suffix);
+    ReturnType operator()(const listElement_& listElement) const
     {
         return list_{listElement.m_name};
     }
 
-    auto operator()(const leafListElement_& leafListElement) const
+    ReturnType operator()(const leafListElement_& leafListElement) const
     {
         return leafList_{leafListElement.m_name};
     }
 
     template <typename T>
-    auto operator()(const T& suffix) const
+    ReturnType operator()(const T& suffix) const
     {
         return suffix;
     }
@@ -239,7 +239,7 @@
 {
     schemaNode_ res;
     res.m_prefix = node.m_prefix;
-    res.m_suffix = boost::apply_visitor(dataSuffixToSchemaSuffix(), node.m_suffix);
+    res.m_suffix = std::visit(dataSuffixToSchemaSuffix(), node.m_suffix);
     return res;
 }
 
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index f768461..b1b949c 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -10,8 +10,8 @@
 #include <boost/fusion/adapted/struct/adapt_struct.hpp>
 #include <boost/fusion/include/adapt_struct.hpp>
 #include <boost/fusion/include/std_pair.hpp>
-#include <boost/variant/variant.hpp>
 #include <map>
+#include <variant>
 #include <vector>
 
 #include "ast_values.hpp"
@@ -83,7 +83,7 @@
 
 struct schemaNode_ {
     boost::optional<module_> m_prefix;
-    boost::variant<container_, list_, nodeup_, leaf_, leafList_> m_suffix;
+    std::variant<container_, list_, nodeup_, leaf_, leafList_> m_suffix;
 
     schemaNode_();
     schemaNode_(decltype(m_suffix) node);
@@ -93,7 +93,7 @@
 
 struct dataNode_ {
     boost::optional<module_> m_prefix;
-    boost::variant<container_, listElement_, nodeup_, leaf_, leafListElement_, leafList_, list_> m_suffix;
+    std::variant<container_, listElement_, nodeup_, leaf_, leafListElement_, leafList_, list_> m_suffix;
 
     dataNode_();
     dataNode_(decltype(m_suffix) node);
diff --git a/src/grammars.hpp b/src/grammars.hpp
index 02d6666..253a16a 100644
--- a/src/grammars.hpp
+++ b/src/grammars.hpp
@@ -74,7 +74,7 @@
     command_names_table()
     {
         boost::mpl::for_each<CommandTypes, boost::type<boost::mpl::_>>([this](auto cmd) {
-            add(commandNamesVisitor()(cmd), decltype(help_::m_cmd)(cmd));
+            add(commandNamesVisitor(cmd), decltype(help_::m_cmd)(cmd));
         });
     }
 } const command_names;
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 909d863..2d69e63 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -65,9 +65,9 @@
 
 void Interpreter::operator()(const create_& create) const
 {
-    if (create.m_path.m_nodes.back().m_suffix.type() == typeid(listElement_))
+    if (std::holds_alternative<listElement_>(create.m_path.m_nodes.back().m_suffix))
         m_datastore.createListInstance(absolutePathFromCommand(create));
-    else if (create.m_path.m_nodes.back().m_suffix.type() == typeid(leafListElement_))
+    else if (std::holds_alternative<leafListElement_>(create.m_path.m_nodes.back().m_suffix))
         m_datastore.createLeafListInstance(absolutePathFromCommand(create));
     else
         m_datastore.createPresenceContainer(absolutePathFromCommand(create));
@@ -75,9 +75,9 @@
 
 void Interpreter::operator()(const delete_& delet) const
 {
-    if (delet.m_path.m_nodes.back().m_suffix.type() == typeid(container_))
+    if (std::holds_alternative<container_>(delet.m_path.m_nodes.back().m_suffix))
         m_datastore.deletePresenceContainer(absolutePathFromCommand(delet));
-    else if (delet.m_path.m_nodes.back().m_suffix.type() == typeid(leafListElement_))
+    else if (std::holds_alternative<leafListElement_>(delet.m_path.m_nodes.back().m_suffix))
         m_datastore.deleteLeafListInstance(absolutePathFromCommand(delet));
     else
         m_datastore.deleteListInstance(absolutePathFromCommand(delet));
@@ -231,6 +231,11 @@
 }
 
 struct pathToStringVisitor : boost::static_visitor<std::string> {
+    std::string operator()(const module_& path) const
+    {
+        using namespace std::string_literals;
+        return "/"s + boost::get<module_>(path).m_name + ":*";
+    }
     std::string operator()(const schemaPath_& path) const
     {
         return pathToSchemaString(path, Prefixes::WhenNeeded);
@@ -242,6 +247,11 @@
 };
 
 struct getPathScopeVisitor : boost::static_visitor<Scope> {
+    Scope operator()(const module_&) const
+    {
+        throw std::logic_error("Interpreter: a top-level module has no scope.");
+    }
+
     template <typename T>
     Scope operator()(const T& path) const
     {
@@ -258,11 +268,10 @@
 
     const auto path = *get.m_path;
     if (path.type() == typeid(module_)) {
-        return "/"s + boost::get<module_>(path).m_name + ":*";
+        return boost::apply_visitor(pathToStringVisitor(), path);
     } else {
-        auto actualPath = boost::get<boost::variant<dataPath_, schemaPath_>>(path);
-        std::string pathString = boost::apply_visitor(pathToStringVisitor(), actualPath);
-        auto pathScope{boost::apply_visitor(getPathScopeVisitor(), actualPath)};
+        std::string pathString = boost::apply_visitor(pathToStringVisitor(), path);
+        auto pathScope{boost::apply_visitor(getPathScopeVisitor(), path)};
 
         if (pathScope == Scope::Absolute) {
             return pathString;
diff --git a/src/parser.cpp b/src/parser.cpp
index 2942d56..195789b 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -88,7 +88,7 @@
         m_curDir = name;
     } else {
         for (const auto& it : name.m_nodes) {
-            if (it.m_suffix.type() == typeid(nodeup_))
+            if (std::holds_alternative<nodeup_>(it.m_suffix))
                 m_curDir.m_nodes.pop_back();
             else
                 m_curDir.m_nodes.push_back(it);
diff --git a/src/parser_context.cpp b/src/parser_context.cpp
index 26e6fa3..6b15cb9 100644
--- a/src/parser_context.cpp
+++ b/src/parser_context.cpp
@@ -43,7 +43,7 @@
 void ParserContext::pushPathFragment(const dataNode_& node)
 {
     auto pushNode = [this] (auto& where, const auto& what) {
-        if (what.m_suffix.type() == typeid(nodeup_)) {
+        if (std::holds_alternative<nodeup_>(what.m_suffix)) {
             where.m_nodes.pop_back();
             if (where.m_nodes.empty()) {
                 m_topLevelModulePresent = false;
diff --git a/src/path_parser.hpp b/src/path_parser.hpp
index ecd3bd7..bb622ef 100644
--- a/src/path_parser.hpp
+++ b/src/path_parser.hpp
@@ -58,9 +58,13 @@
     using type = dataNode_;
 };
 
+enum class CompletionMode {
+    Schema,
+    Data
+};
 
-template <NodeParserMode PARSER_MODE>
-struct NodeParser : x3::parser<NodeParser<PARSER_MODE>> {
+template <NodeParserMode PARSER_MODE, CompletionMode COMPLETION_MODE>
+struct NodeParser : x3::parser<NodeParser<PARSER_MODE, COMPLETION_MODE>> {
     using attribute_type = typename ModeToAttribute<PARSER_MODE>::type;
 
     std::function<bool(const Schema&, const std::string& path)> m_filterFunction;
@@ -114,7 +118,12 @@
                     } else {
                         out.m_suffix = listElement_{child.second, {}};
                     }
-                    parserContext.m_suggestions.emplace(Completion{parseString, "[", Completion::WhenToAdd::IfFullMatch});
+
+                    if constexpr (COMPLETION_MODE == CompletionMode::Schema) {
+                        parserContext.m_suggestions.emplace(Completion{parseString + "/"});
+                    } else {
+                        parserContext.m_suggestions.emplace(Completion{parseString, "[", Completion::WhenToAdd::IfFullMatch});
+                    }
                     break;
                 case yang::NodeTypes::LeafList:
                     if constexpr (std::is_same<attribute_type, schemaNode_>()) {
@@ -122,7 +131,12 @@
                     } else {
                         out.m_suffix = leafListElement_{child.second, {}};
                     }
-                    parserContext.m_suggestions.emplace(Completion{parseString, "[", Completion::WhenToAdd::IfFullMatch});
+
+                    if constexpr (COMPLETION_MODE == CompletionMode::Schema) {
+                        parserContext.m_suggestions.emplace(Completion{parseString + "/"});
+                    } else {
+                        parserContext.m_suggestions.emplace(Completion{parseString, "[", Completion::WhenToAdd::IfFullMatch});
+                    }
                     break;
                 case yang::NodeTypes::Action:
                 case yang::NodeTypes::AnyXml:
@@ -156,19 +170,19 @@
                 parserContext.m_curModule = attr.m_prefix->m_name;
             }
 
-            if (attr.m_suffix.type() == typeid(leaf_)) {
+            if (std::holds_alternative<leaf_>(attr.m_suffix)) {
                 parserContext.m_tmpListKeyLeafPath.m_location = parserContext.currentSchemaPath();
                 ModuleNodePair node{attr.m_prefix.flat_map([](const auto& it) {
                                         return boost::optional<std::string>{it.m_name};
                                     }),
-                                    boost::get<leaf_>(attr.m_suffix).m_name};
+                                    std::get<leaf_>(attr.m_suffix).m_name};
                 parserContext.m_tmpListKeyLeafPath.m_node = node;
             }
 
             if constexpr (std::is_same<attribute_type, dataNode_>()) {
-                if (attr.m_suffix.type() == typeid(listElement_)) {
-                    parserContext.m_tmpListName = boost::get<listElement_>(attr.m_suffix).m_name;
-                    res = listSuffix.parse(begin, end, ctx, rctx, boost::get<listElement_>(attr.m_suffix).m_keys);
+                if (std::holds_alternative<listElement_>(attr.m_suffix)) {
+                    parserContext.m_tmpListName = std::get<listElement_>(attr.m_suffix).m_name;
+                    res = listSuffix.parse(begin, end, ctx, rctx, std::get<listElement_>(attr.m_suffix).m_keys);
 
                     // FIXME: think of a better way to do this, that is, get rid of manual iterator reverting
                     if (!res) {
@@ -176,26 +190,26 @@
                         // If we don't, we fail the whole symbol table.
                         if constexpr (PARSER_MODE == NodeParserMode::IncompleteDataNode) {
                             res = true;
-                            attr.m_suffix = list_{boost::get<listElement_>(attr.m_suffix).m_name};
+                            attr.m_suffix = list_{std::get<listElement_>(attr.m_suffix).m_name};
                         } else {
                             begin = saveIter;
                         }
                     }
                 }
 
-                if (attr.m_suffix.type() == typeid(leafListElement_)) {
+                if (std::holds_alternative<leafListElement_>(attr.m_suffix)) {
                     parserContext.m_tmpListKeyLeafPath.m_location = parserContext.currentSchemaPath();
                     ModuleNodePair node{attr.m_prefix.flat_map([](const auto& it) {
                                             return boost::optional<std::string>{it.m_name};
                                         }),
-                                        boost::get<leafListElement_>(attr.m_suffix).m_name};
+                                        std::get<leafListElement_>(attr.m_suffix).m_name};
                     parserContext.m_tmpListKeyLeafPath.m_node = node;
-                    res = leafListValue.parse(begin, end, ctx, rctx, boost::get<leafListElement_>(attr.m_suffix).m_value);
+                    res = leafListValue.parse(begin, end, ctx, rctx, std::get<leafListElement_>(attr.m_suffix).m_value);
 
                     if (!res) {
                         if constexpr (PARSER_MODE == NodeParserMode::IncompleteDataNode) {
                             res = true;
-                            attr.m_suffix = leafList_{boost::get<leafListElement_>(attr.m_suffix).m_name};
+                            attr.m_suffix = leafList_{std::get<leafListElement_>(attr.m_suffix).m_name};
                         } else {
                             begin = saveIter;
                         }
@@ -216,10 +230,10 @@
     }
 };
 
-using schemaNode = NodeParser<NodeParserMode::SchemaNode>;
-using dataNode = NodeParser<NodeParserMode::CompleteDataNode>;
-using incompleteDataNode = NodeParser<NodeParserMode::IncompleteDataNode>;
-using pathCompletions = NodeParser<NodeParserMode::CompletionsOnly>;
+template <CompletionMode COMPLETION_MODE> using schemaNode = NodeParser<NodeParserMode::SchemaNode, COMPLETION_MODE>;
+template <CompletionMode COMPLETION_MODE> using dataNode = NodeParser<NodeParserMode::CompleteDataNode, COMPLETION_MODE>;
+template <CompletionMode COMPLETION_MODE> using incompleteDataNode = NodeParser<NodeParserMode::IncompleteDataNode, COMPLETION_MODE>;
+template <CompletionMode COMPLETION_MODE> using pathCompletions = NodeParser<NodeParserMode::CompletionsOnly, COMPLETION_MODE>;
 
 using AnyPath = boost::variant<schemaPath_, dataPath_>;
 
@@ -244,8 +258,8 @@
     using type = dataPath_;
 };
 
-template <PathParserMode PARSER_MODE>
-struct PathParser : x3::parser<PathParser<PARSER_MODE>> {
+template <PathParserMode PARSER_MODE, CompletionMode COMPLETION_MODE>
+struct PathParser : x3::parser<PathParser<PARSER_MODE, COMPLETION_MODE>> {
     using attribute_type = ModeToAttribute<PARSER_MODE>;
     std::function<bool(const Schema&, const std::string& path)> m_filterFunction;
 
@@ -266,7 +280,7 @@
         // gets reverted to before the starting slash.
         auto res = (-absoluteStart).parse(begin, end, ctx, rctx, attrData.m_scope);
         auto dataPath = x3::attr(attrData.m_scope)
-            >> (dataNode{m_filterFunction} % '/' | pathEnd >> x3::attr(std::vector<dataNode_>{}))
+            >> (dataNode<COMPLETION_MODE>{m_filterFunction} % '/' | pathEnd >> x3::attr(std::vector<dataNode_>{}))
             >> -trailingSlash;
         res = dataPath.parse(begin, end, ctx, rctx, attrData);
 
@@ -274,13 +288,13 @@
         if constexpr (PARSER_MODE == PathParserMode::DataPathListEnd || PARSER_MODE == PathParserMode::AnyPath) {
             if (!res || !pathEnd.parse(begin, end, ctx, rctx, x3::unused)) {
                 dataNode_ attrNodeList;
-                res = incompleteDataNode{m_filterFunction}.parse(begin, end, ctx, rctx, attrNodeList);
+                res = incompleteDataNode<COMPLETION_MODE>{m_filterFunction}.parse(begin, end, ctx, rctx, attrNodeList);
                 if (res) {
                     attrData.m_nodes.push_back(attrNodeList);
                     // If the trailing slash matches, no more nodes are parsed.
                     // That means no more completion. So, I generate them
                     // manually.
-                    res = (-(trailingSlash >> x3::omit[pathCompletions{m_filterFunction}])).parse(begin, end, ctx, rctx, attrData.m_trailingSlash);
+                    res = (-(trailingSlash >> x3::omit[pathCompletions<COMPLETION_MODE>{m_filterFunction}])).parse(begin, end, ctx, rctx, attrData.m_trailingSlash);
                 }
             }
         }
@@ -289,13 +303,13 @@
         if constexpr (PARSER_MODE == PathParserMode::AnyPath) {
             // If our data path already has some listElement_ fragments, we can't parse rest of the path as a schema path
             auto hasLists = std::any_of(attrData.m_nodes.begin(), attrData.m_nodes.end(),
-                [] (const auto& node) { return node.m_suffix.type() == typeid(listElement_); });
+                [] (const auto& node) { return std::holds_alternative<listElement_>(node.m_suffix); });
             // If parsing failed, or if there's more input we try parsing schema nodes.
             if (!hasLists) {
                 if (!res || !pathEnd.parse(begin, end, ctx, rctx, x3::unused)) {
                     // If dataPath parsed some nodes, they will be saved in `attrData`. We have to keep these.
                     schemaPath_ attrSchema = dataPathToSchemaPath(attrData);
-                    auto schemaPath = schemaNode{m_filterFunction} % '/';
+                    auto schemaPath = schemaNode<COMPLETION_MODE>{m_filterFunction} % '/';
                     // The schemaPath parser continues where the dataPath parser ended.
                     res = schemaPath.parse(begin, end, ctx, rctx, attrSchema.m_nodes);
                     auto trailing = -trailingSlash >> pathEnd;
@@ -315,9 +329,9 @@
 // The PathParser class would get a boost::variant as the attribute, but I
 // don't want to deal with that, so I use these wrappers to ensure the
 // attribute I want (and let Spirit deal with boost::variant).
-auto const anyPath = x3::rule<class anyPath_class, AnyPath>{"anyPath"} = PathParser<PathParserMode::AnyPath>{};
-auto const dataPath = x3::rule<class dataPath_class, dataPath_>{"dataPath"} = PathParser<PathParserMode::DataPath>{};
-auto const dataPathListEnd = x3::rule<class dataPath_class, dataPath_>{"dataPath"} = PathParser<PathParserMode::DataPathListEnd>{};
+auto const anyPath = x3::rule<class anyPath_class, AnyPath>{"anyPath"} = PathParser<PathParserMode::AnyPath, CompletionMode::Schema>{};
+auto const dataPath = x3::rule<class dataPath_class, dataPath_>{"dataPath"} = PathParser<PathParserMode::DataPath, CompletionMode::Data>{};
+auto const dataPathListEnd = x3::rule<class dataPath_class, dataPath_>{"dataPath"} = PathParser<PathParserMode::DataPathListEnd, CompletionMode::Data>{};
 
 #if __clang__
 #pragma GCC diagnostic push
@@ -381,7 +395,7 @@
 };
 
 auto const writableLeafPath_def =
-    PathParser<PathParserMode::DataPath>{filterConfigFalse};
+    PathParser<PathParserMode::DataPath, CompletionMode::Data>{filterConfigFalse};
 
 auto const presenceContainerPath_def =
     dataPath;
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index a1a5602..5fdc42a 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -20,15 +20,6 @@
     return m_nodes.at(name);
 }
 
-bool StaticSchema::nodeExists(const std::string& location, const std::string& node) const
-{
-    if (node.empty())
-        return true;
-    const auto& childrenRef = children(location);
-
-    return childrenRef.find(node) != childrenRef.end();
-}
-
 bool StaticSchema::isModule(const std::string& name) const
 {
     return m_modules.find(name) != m_modules.end();
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
index aab4383..0426f02 100644
--- a/src/static_schema.hpp
+++ b/src/static_schema.hpp
@@ -89,7 +89,6 @@
 private:
     const std::unordered_map<std::string, NodeInfo>& children(const std::string& name) const;
     void getIdentSet(const identityRef_& ident, std::set<identityRef_>& res) const;
-    bool nodeExists(const std::string& location, const std::string& node) const;
 
     std::unordered_map<std::string, std::unordered_map<std::string, NodeInfo>> m_nodes;
     std::set<std::string> m_modules;
diff --git a/tests/path_completion.cpp b/tests/path_completion.cpp
index 71fc917..3002ed7 100644
--- a/tests/path_completion.cpp
+++ b/tests/path_completion.cpp
@@ -38,6 +38,7 @@
     schema->addLeaf("/example:twoKeyList", "example:number", yang::Int32{});
     schema->addLeaf("/", "example:leafInt", yang::Int32{});
     schema->addLeaf("/", "example:readonly", yang::Int32{}, yang::AccessType::ReadOnly);
+    schema->addLeafList("/", "example:addresses", yang::String{});
     auto mockDatastore = std::make_shared<MockDatastoreAccess>();
 
     // The parser will use DataQuery for key value completion, but I'm not testing that here, so I don't return anything.
@@ -67,14 +68,14 @@
         SECTION("ls ")
         {
             input = "ls ";
-            expectedCompletions = {"example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list", "example:ovoce", "example:readonly ", "example:ovocezelenina", "example:twoKeyList", "second:amelie/"};
+            expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/", "second:amelie/"};
             expectedContextLength = 0;
         }
 
         SECTION("ls e")
         {
             input = "ls e";
-            expectedCompletions = {"example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list", "example:ovoce", "example:readonly ", "example:ovocezelenina", "example:twoKeyList"};
+            expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/"};
             expectedContextLength = 1;
         }
 
@@ -102,14 +103,14 @@
         SECTION("ls /")
         {
             input = "ls /";
-            expectedCompletions = {"example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list", "example:ovoce", "example:readonly ", "example:ovocezelenina", "example:twoKeyList", "second:amelie/"};
+            expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/", "second:amelie/"};
             expectedContextLength = 0;
         }
 
         SECTION("ls /e")
         {
             input = "ls /e";
-            expectedCompletions = {"example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list", "example:ovoce", "example:readonly ", "example:ovocezelenina", "example:twoKeyList"};
+            expectedCompletions = {"example:addresses/", "example:ano/", "example:anoda/", "example:bota/", "example:leafInt ", "example:list/", "example:ovoce/", "example:readonly ", "example:ovocezelenina/", "example:twoKeyList/"};
             expectedContextLength = 1;
         }
 
@@ -134,6 +135,13 @@
             expectedContextLength = 1;
         }
 
+        SECTION("ls /example:list")
+        {
+            input = "ls /example:list";
+            expectedCompletions = {"example:list/"};
+            expectedContextLength = 12;
+        }
+
         SECTION("ls /example:list/")
         {
             input = "ls /example:list/";