Don't use down_cast<> with the assumption that type-mismatch returns null.

We typically don't compile-in RTTI so relying on down_cast<> returning
nullptr or a correct type will result in subtle issues.

Instead, provide MaybeNode() and MaybeLeaf() that use the SymbolKind
to provide such casts.

This should be expanded:
  * Ideally we remove _all_ uses of down_cast<> and use type-safe
    ways of doing the same thing (and then: remove verible::down_cast<>).
  * There are a few places where we CHECK() fail on type-mismatch, assuming
    these can't happen; but that depends on how valid our syntax tree is,
    which depends on the input the the parser to reject such input. These
    should be reformulated with graceful error handling.

Fixes #2419
diff --git a/verible/common/analysis/matcher/BUILD b/verible/common/analysis/matcher/BUILD
index d4a1715..2d93eb9 100644
--- a/verible/common/analysis/matcher/BUILD
+++ b/verible/common/analysis/matcher/BUILD
@@ -17,7 +17,7 @@
     hdrs = ["bound-symbol-manager.h"],
     deps = [
         "//verible/common/text:symbol",
-        "//verible/common/util:casts",
+        "//verible/common/text:tree-utils",
         "//verible/common/util:container-util",
         "//verible/common/util:logging",
     ],
diff --git a/verible/common/analysis/matcher/bound-symbol-manager.h b/verible/common/analysis/matcher/bound-symbol-manager.h
index 0feca14..46ef0e4 100644
--- a/verible/common/analysis/matcher/bound-symbol-manager.h
+++ b/verible/common/analysis/matcher/bound-symbol-manager.h
@@ -19,9 +19,11 @@
 #include <string>
 
 #include "verible/common/text/symbol.h"
-#include "verible/common/util/casts.h"
+#include "verible/common/text/tree-utils.h"
 
 namespace verible {
+class SyntaxTreeNode;
+class SyntaxTreeLeaf;
 namespace matcher {
 
 // Manages sets of Bound Symbols created when matching against a syntax tree.
@@ -52,9 +54,14 @@
     return bound_symbols_;
   }
 
-  template <typename T>
-  const T *GetAs(const std::string &key) const {
-    return down_cast<const T *>(FindSymbol(key));
+  // Return named node as Node if available of of that type.
+  const SyntaxTreeNode *GetAsNode(const std::string &key) const {
+    return verible::MaybeNode(FindSymbol(key));
+  }
+
+  // Return named node as Node if available of of that type.
+  const SyntaxTreeLeaf *GetAsLeaf(const std::string &key) const {
+    return verible::MaybeLeaf(FindSymbol(key));
   }
 
  private:
diff --git a/verible/common/text/symbol.h b/verible/common/text/symbol.h
index 4a349da..ad8e95f 100644
--- a/verible/common/text/symbol.h
+++ b/verible/common/text/symbol.h
@@ -55,8 +55,6 @@
 }
 constexpr SymbolTag LeafTag(int tag) { return {SymbolKind::kLeaf, tag}; }
 
-// forward declare Visitor classes to allow references in Symbol
-
 class Symbol {
  public:
   virtual ~Symbol() = default;
diff --git a/verible/common/text/tree-utils.cc b/verible/common/text/tree-utils.cc
index cadd313..b76be7b 100644
--- a/verible/common/text/tree-utils.cc
+++ b/verible/common/text/tree-utils.cc
@@ -126,6 +126,15 @@
   return down_cast<const SyntaxTreeLeaf &>(symbol);
 }
 
+const SyntaxTreeNode *MaybeNode(const Symbol *symbol) {
+  if (!symbol || symbol->Kind() != SymbolKind::kNode) return nullptr;
+  return static_cast<const SyntaxTreeNode *>(symbol);
+}
+const SyntaxTreeLeaf *MaybeLeaf(const Symbol *symbol) {
+  if (!symbol || symbol->Kind() != SymbolKind::kLeaf) return nullptr;
+  return static_cast<const SyntaxTreeLeaf *>(symbol);
+}
+
 namespace {
 // FirstSubtreeFinderMutable is a visitor class that supports the implementation
 // of FindFirstSubtreeMutable().  It is derived from
diff --git a/verible/common/text/tree-utils.h b/verible/common/text/tree-utils.h
index 8a18924..475295b 100644
--- a/verible/common/text/tree-utils.h
+++ b/verible/common/text/tree-utils.h
@@ -30,6 +30,9 @@
 #include "verible/common/util/logging.h"
 #include "verible/common/util/type-traits.h"
 
+// TODO: the functions below that can crash while asserting a type should
+// probably return a usable error (std::optional).
+
 namespace verible {
 
 // Returns the leftmost/rightmost leaf contained in Symbol.
@@ -47,6 +50,7 @@
 std::string_view StringSpanOfSymbol(const Symbol &lsym, const Symbol &rsym);
 
 // Returns a SyntaxTreeNode down_casted from a Symbol.
+// Will panic if not of that kind.
 const SyntaxTreeNode &SymbolCastToNode(const Symbol &);
 // Mutable variant.
 SyntaxTreeNode &SymbolCastToNode(Symbol &);  // NOLINT
@@ -59,8 +63,15 @@
 inline SyntaxTreeNode &SymbolCastToNode(SyntaxTreeNode &node) { return node; }
 
 // Returns a SyntaxTreeLeaf down_casted from a Symbol.
+// Will panic if not of that kind.
 const SyntaxTreeLeaf &SymbolCastToLeaf(const Symbol &);
 
+// Return Node if symbol is of that kind, otherwise nullptr.
+const SyntaxTreeNode *MaybeNode(const Symbol *symbol);
+
+// Return Leaf if symbol is of that kind, otherwise nullptr.
+const SyntaxTreeLeaf *MaybeLeaf(const Symbol *symbol);
+
 // Unwrap layers of only-child nodes until reaching a leaf or a node with
 // multiple children.
 const Symbol *DescendThroughSingletons(const Symbol &symbol);
diff --git a/verible/verilog/CST/BUILD b/verible/verilog/CST/BUILD
index a2ecea2..98f4c9b 100644
--- a/verible/verilog/CST/BUILD
+++ b/verible/verilog/CST/BUILD
@@ -98,6 +98,7 @@
         "//verible/common/text:concrete-syntax-tree",
         "//verible/common/text:symbol",
         "//verible/common/text:token-info",
+        "//verible/common/text:tree-utils",
         "//verible/common/util:logging",
         "@abseil-cpp//absl/strings",
     ],
diff --git a/verible/verilog/CST/expression.cc b/verible/verilog/CST/expression.cc
index 39d75de..7256626 100644
--- a/verible/verilog/CST/expression.cc
+++ b/verible/verilog/CST/expression.cc
@@ -104,8 +104,9 @@
     return nullptr;
   }
   const verible::Symbol *leaf_symbol = node->front().get();
-  return &verible::down_cast<const verible::SyntaxTreeLeaf *>(leaf_symbol)
-              ->get();
+  const verible::SyntaxTreeLeaf *leaf = verible::MaybeLeaf(leaf_symbol);
+  if (!leaf) return nullptr;
+  return &leaf->get();
 }
 
 const verible::Symbol *GetUnaryPrefixOperand(const verible::Symbol &symbol) {
diff --git a/verible/verilog/CST/identifier.cc b/verible/verilog/CST/identifier.cc
index e5adc82..9f03a0c 100644
--- a/verible/verilog/CST/identifier.cc
+++ b/verible/verilog/CST/identifier.cc
@@ -72,8 +72,7 @@
     return nullptr;
   }
   const auto &node = down_cast<const verible::SyntaxTreeNode &>(symbol);
-  const auto *leaf = down_cast<const verible::SyntaxTreeLeaf *>(node[0].get());
-  return leaf;
+  return verible::MaybeLeaf(node[0].get());
 }
 
 const verible::SyntaxTreeLeaf *AutoUnwrapIdentifier(
diff --git a/verible/verilog/CST/verilog-treebuilder-utils.cc b/verible/verilog/CST/verilog-treebuilder-utils.cc
index 00af7e6..91eea0f 100644
--- a/verible/verilog/CST/verilog-treebuilder-utils.cc
+++ b/verible/verilog/CST/verilog-treebuilder-utils.cc
@@ -22,12 +22,11 @@
 #include "verible/common/text/concrete-syntax-tree.h"
 #include "verible/common/text/symbol.h"
 #include "verible/common/text/token-info.h"
+#include "verible/common/text/tree-utils.h"
 #include "verible/common/util/logging.h"
 
 namespace verilog {
 
-using verible::down_cast;
-
 // Set of utility functions for embedded a statement into a certain context.
 std::string EmbedInClass(std::string_view text) {
   return absl::StrCat("class test_class;\n", text, "\nendclass\n");
@@ -47,7 +46,7 @@
 }
 
 void ExpectString(const verible::SymbolPtr &symbol, std::string_view expected) {
-  const auto *leaf = down_cast<const verible::SyntaxTreeLeaf *>(symbol.get());
+  const verible::SyntaxTreeLeaf *leaf = verible::MaybeLeaf(symbol.get());
   CHECK(leaf != nullptr) << "expected: " << expected;
   CHECK_EQ(leaf->get().text(), expected);
 }
diff --git a/verible/verilog/analysis/checkers/BUILD b/verible/verilog/analysis/checkers/BUILD
index 98178d9..5ecdd20 100644
--- a/verible/verilog/analysis/checkers/BUILD
+++ b/verible/verilog/analysis/checkers/BUILD
@@ -931,6 +931,7 @@
         "//verible/common/text:symbol",
         "//verible/common/text:syntax-tree-context",
         "//verible/common/text:token-info",
+        "//verible/common/text:tree-utils",
         "//verible/common/util:logging",
         "//verible/verilog/CST:numbers",
         "//verible/verilog/CST:verilog-matchers",
@@ -971,6 +972,7 @@
         "//verible/common/text:symbol",
         "//verible/common/text:syntax-tree-context",
         "//verible/common/text:token-info",
+        "//verible/common/text:tree-utils",
         "//verible/verilog/CST:numbers",
         "//verible/verilog/CST:verilog-matchers",
         "//verible/verilog/analysis:descriptions",
@@ -1010,7 +1012,7 @@
         "//verible/common/text:symbol",
         "//verible/common/text:syntax-tree-context",
         "//verible/common/text:token-info",
-        "//verible/common/util:casts",
+        "//verible/common/text:tree-utils",
         "//verible/verilog/CST:expression",
         "//verible/verilog/CST:verilog-matchers",
         "//verible/verilog/CST:verilog-nonterminals",
@@ -1156,7 +1158,6 @@
         "//verible/common/text:symbol",
         "//verible/common/text:syntax-tree-context",
         "//verible/common/text:tree-utils",
-        "//verible/common/util:casts",
         "//verible/verilog/CST:verilog-matchers",
         "//verible/verilog/CST:verilog-nonterminals",
         "//verible/verilog/analysis:descriptions",
@@ -1195,7 +1196,7 @@
         "//verible/common/text:config-utils",
         "//verible/common/text:symbol",
         "//verible/common/text:syntax-tree-context",
-        "//verible/common/util:casts",
+        "//verible/common/text:tree-utils",
         "//verible/common/util:logging",
         "//verible/verilog/CST:verilog-matchers",
         "//verible/verilog/CST:verilog-nonterminals",
diff --git a/verible/verilog/analysis/checkers/always-comb-blocking-rule.cc b/verible/verilog/analysis/checkers/always-comb-blocking-rule.cc
index 0988b8d..68e0509 100644
--- a/verible/verilog/analysis/checkers/always-comb-blocking-rule.cc
+++ b/verible/verilog/analysis/checkers/always-comb-blocking-rule.cc
@@ -27,7 +27,6 @@
 #include "verible/common/text/symbol.h"
 #include "verible/common/text/syntax-tree-context.h"
 #include "verible/common/text/tree-utils.h"
-#include "verible/common/util/casts.h"
 #include "verible/verilog/CST/verilog-matchers.h"  // IWYU pragma: keep
 #include "verible/verilog/CST/verilog-nonterminals.h"
 #include "verible/verilog/analysis/descriptions.h"
@@ -38,7 +37,6 @@
 namespace analysis {
 
 using verible::AutoFix;
-using verible::down_cast;
 using verible::LintRuleStatus;
 using verible::LintViolation;
 using verible::SearchSyntaxTree;
@@ -77,8 +75,8 @@
          SearchSyntaxTree(symbol, NodekNonblockingAssignmentStatement())) {
       if (match.match->Kind() != verible::SymbolKind::kNode) continue;
 
-      const auto *node =
-          down_cast<const verible::SyntaxTreeNode *>(match.match);
+      const verible::SyntaxTreeNode *node = verible::MaybeNode(match.match);
+      if (!node) return;
 
       const verible::SyntaxTreeLeaf *leaf = verible::GetSubtreeAsLeaf(
           *node, NodeEnum::kNonblockingAssignmentStatement, 1);
diff --git a/verible/verilog/analysis/checkers/always-ff-non-blocking-rule.cc b/verible/verilog/analysis/checkers/always-ff-non-blocking-rule.cc
index 3b0a4db..4922c52 100644
--- a/verible/verilog/analysis/checkers/always-ff-non-blocking-rule.cc
+++ b/verible/verilog/analysis/checkers/always-ff-non-blocking-rule.cc
@@ -29,7 +29,7 @@
 #include "verible/common/text/config-utils.h"
 #include "verible/common/text/symbol.h"
 #include "verible/common/text/syntax-tree-context.h"
-#include "verible/common/util/casts.h"
+#include "verible/common/text/tree-utils.h"
 #include "verible/common/util/logging.h"
 #include "verible/verilog/CST/verilog-matchers.h"
 #include "verible/verilog/CST/verilog-nonterminals.h"
@@ -108,22 +108,18 @@
 
   verible::matcher::BoundSymbolManager symbol_man;
   if (asgn_blocking_matcher.Matches(symbol, &symbol_man)) {
-    if (const auto *const node =
-            verible::down_cast<const verible::SyntaxTreeNode *>(&symbol)) {
-      check_root =
-          /* lhs */ verible::down_cast<const verible::SyntaxTreeNode *>(
-              node->front().get());
+    if (const verible::SyntaxTreeNode *const node =
+            verible::MaybeNode(&symbol)) {
+      check_root = /* lhs */ verible::MaybeNode(node->front().get());
     }
   } else {
     // Not interested in any other blocking assignments unless flagged
     if (!catch_modifying_assignments_) return;
 
     if (asgn_modify_matcher.Matches(symbol, &symbol_man)) {
-      if (const auto *const node =
-              verible::down_cast<const verible::SyntaxTreeNode *>(&symbol)) {
-        check_root =
-            /* lhs */ verible::down_cast<const verible::SyntaxTreeNode *>(
-                node->front().get());
+      if (const verible::SyntaxTreeNode *const node =
+              verible::MaybeNode(&symbol)) {
+        check_root = /* lhs */ verible::MaybeNode(node->front().get());
       }
     } else if (asgn_incdec_matcher.Matches(symbol, &symbol_man)) {
       check_root = &symbol;
@@ -144,11 +140,9 @@
       if (var.context.IsInside(NodeEnum::kHierarchyExtension)) continue;
 
       bool found = false;
-      if (const auto *const varn =
-              verible::down_cast<const verible::SyntaxTreeNode *>(var.match)) {
-        if (const auto *const ident =
-                verible::down_cast<const verible::SyntaxTreeLeaf *>(
-                    varn->front().get())) {
+      if (const verible::SyntaxTreeNode *const varn =
+              verible::MaybeNode(var.match)) {
+        if (const auto *const ident = verible::MaybeLeaf(varn->front().get())) {
           found = std::find(locals_.begin(), locals_.end(),
                             ident->get().text()) != locals_.end();
           VLOG(4) << "LHS='" << ident->get().text() << "' FOUND=" << found
@@ -210,11 +204,10 @@
   if (decl_matcher.Matches(symbol, &symbol_man)) {
     auto &count = scopes_.top().inherited_local_count;
     for (const auto &var : SearchSyntaxTree(symbol, var_matcher)) {
-      if (const auto *const node =
-              verible::down_cast<const verible::SyntaxTreeNode *>(var.match)) {
-        if (const auto *const ident =
-                verible::down_cast<const verible::SyntaxTreeLeaf *>(
-                    node->front().get())) {
+      if (const verible::SyntaxTreeNode *const node =
+              verible::MaybeNode(var.match)) {
+        if (const verible::SyntaxTreeLeaf *const ident =
+                verible::MaybeLeaf(node->front().get())) {
           const std::string_view name = ident->get().text();
           VLOG(4) << "Registering '" << name << '\'' << std::endl;
           locals_.emplace_back(name);
diff --git a/verible/verilog/analysis/checkers/create-object-name-match-rule.cc b/verible/verilog/analysis/checkers/create-object-name-match-rule.cc
index 9e5fed9..cc014f4 100644
--- a/verible/verilog/analysis/checkers/create-object-name-match-rule.cc
+++ b/verible/verilog/analysis/checkers/create-object-name-match-rule.cc
@@ -29,7 +29,7 @@
 #include "verible/common/text/symbol.h"
 #include "verible/common/text/syntax-tree-context.h"
 #include "verible/common/text/token-info.h"
-#include "verible/common/util/casts.h"
+#include "verible/common/text/tree-utils.h"
 #include "verible/verilog/CST/expression.h"
 #include "verible/verilog/CST/verilog-matchers.h"
 #include "verible/verilog/CST/verilog-nonterminals.h"
@@ -40,7 +40,6 @@
 namespace verilog {
 namespace analysis {
 
-using verible::down_cast;
 using verible::LintRuleStatus;
 using verible::LintViolation;
 using verible::SyntaxTreeContext;
@@ -89,8 +88,7 @@
   if (node.MatchesTag(NodeEnum::kUnqualifiedId)) {
     if (!node.empty()) {
       // The one-and-only child is the SymbolIdentifier token
-      const auto &leaf_ptr =
-          down_cast<const SyntaxTreeLeaf *>(node.front().get());
+      const SyntaxTreeLeaf *leaf_ptr = verible::MaybeLeaf(node.front().get());
       if (leaf_ptr != nullptr) {
         const TokenInfo &token = leaf_ptr->get();
         return token.token_enum() == SymbolIdentifier && token.text() == name;
@@ -109,10 +107,11 @@
   // my_pkg::class_type::type_id::create.
   // 5: 3 segments + 2 separators (in alternation), e.g. A::B::C
   if (qualified_id_node.size() >= 5) {
-    const auto *create_leaf_ptr =
-        down_cast<const SyntaxTreeNode *>(qualified_id_node.back().get());
-    const auto *type_id_leaf_ptr = down_cast<const SyntaxTreeNode *>(
-        qualified_id_node[num_children - 3].get());
+    // TODO: naming is off. These are not leafs
+    const SyntaxTreeNode *create_leaf_ptr =
+        verible::MaybeNode(qualified_id_node.back().get());
+    const SyntaxTreeNode *type_id_leaf_ptr =
+        verible::MaybeNode(qualified_id_node[num_children - 3].get());
     if (create_leaf_ptr != nullptr && type_id_leaf_ptr != nullptr) {
       return UnqualifiedIdEquals(*create_leaf_ptr, "create") &&
              UnqualifiedIdEquals(*type_id_leaf_ptr, "type_id");
@@ -138,12 +137,7 @@
   if (!expr_node.MatchesTag(NodeEnum::kExpression)) return nullptr;
 
   // this check is limited to only checking string literal leaf tokens
-  if (expr_node.front().get()->Kind() != verible::SymbolKind::kLeaf) {
-    return nullptr;
-  }
-
-  const auto *leaf_ptr =
-      down_cast<const SyntaxTreeLeaf *>(expr_node.front().get());
+  const SyntaxTreeLeaf *leaf_ptr = verible::MaybeLeaf(expr_node.front().get());
   if (leaf_ptr != nullptr) {
     const TokenInfo &token = leaf_ptr->get();
     if (token.token_enum() == TK_StringLiteral) {
@@ -158,8 +152,8 @@
     const SyntaxTreeNode &args_node) {
   if (!args_node.empty()) {
     const auto &first_arg = args_node.front();
-    if (const auto *first_expr =
-            down_cast<const SyntaxTreeNode *>(first_arg.get())) {
+    if (const SyntaxTreeNode *first_expr =
+            verible::MaybeNode(first_arg.get())) {
       return first_expr;
     }
   }
@@ -183,15 +177,15 @@
 
   // Extract named bindings for matched nodes within this match.
 
-  const auto *lval_ref = manager.GetAs<SyntaxTreeNode>("lval_ref");
+  const SyntaxTreeNode *lval_ref = manager.GetAsNode("lval_ref");
   if (lval_ref == nullptr) return;
 
   const TokenInfo *lval_id = ReferenceIsSimpleIdentifier(*lval_ref);
   if (lval_id == nullptr) return;
   if (lval_id->token_enum() != SymbolIdentifier) return;
 
-  const auto *call = manager.GetAs<SyntaxTreeNode>("func");
-  const auto *args = manager.GetAs<SyntaxTreeNode>("args");
+  const SyntaxTreeNode *call = manager.GetAsNode("func");
+  const SyntaxTreeNode *args = manager.GetAsNode("args");
   if (call == nullptr) return;
   if (args == nullptr) return;
   if (!QualifiedCallIsTypeIdCreate(*call)) return;
diff --git a/verible/verilog/analysis/checkers/forbidden-macro-rule.cc b/verible/verilog/analysis/checkers/forbidden-macro-rule.cc
index e403106..34c3da1 100644
--- a/verible/verilog/analysis/checkers/forbidden-macro-rule.cc
+++ b/verible/verilog/analysis/checkers/forbidden-macro-rule.cc
@@ -71,7 +71,7 @@
     const verible::Symbol &symbol, const verible::SyntaxTreeContext &context) {
   verible::matcher::BoundSymbolManager manager;
   if (MacroCallMatcher().Matches(symbol, &manager)) {
-    if (const auto *leaf = manager.GetAs<verible::SyntaxTreeLeaf>("name")) {
+    if (const verible::SyntaxTreeLeaf *leaf = manager.GetAsLeaf("name")) {
       const auto &imm = InvalidMacrosMap();
       if (imm.find(std::string(leaf->get().text())) != imm.end()) {
         violations_.insert(
diff --git a/verible/verilog/analysis/checkers/forbidden-symbol-rule.cc b/verible/verilog/analysis/checkers/forbidden-symbol-rule.cc
index 24d0580..04558ea 100644
--- a/verible/verilog/analysis/checkers/forbidden-symbol-rule.cc
+++ b/verible/verilog/analysis/checkers/forbidden-symbol-rule.cc
@@ -79,7 +79,7 @@
     const verible::Symbol &symbol, const verible::SyntaxTreeContext &context) {
   verible::matcher::BoundSymbolManager manager;
   if (IdMatcher().Matches(symbol, &manager)) {
-    if (const auto *leaf = manager.GetAs<verible::SyntaxTreeLeaf>("name")) {
+    if (const verible::SyntaxTreeLeaf *leaf = manager.GetAsLeaf("name")) {
       const auto &ism = InvalidSymbolsMap();
       if (ism.find(std::string(leaf->get().text())) != ism.end()) {
         violations_.insert(
diff --git a/verible/verilog/analysis/checkers/module-instantiation-rules.cc b/verible/verilog/analysis/checkers/module-instantiation-rules.cc
index 1639ef2..9a850e5 100644
--- a/verible/verilog/analysis/checkers/module-instantiation-rules.cc
+++ b/verible/verilog/analysis/checkers/module-instantiation-rules.cc
@@ -92,16 +92,14 @@
 }
 
 static bool IsComma(const verible::Symbol &symbol) {
-  if (symbol.Kind() == verible::SymbolKind::kLeaf) {
-    const auto *leaf = down_cast<const verible::SyntaxTreeLeaf *>(&symbol);
-    if (leaf) return leaf->get().token_enum() == ',';
-  }
+  const verible::SyntaxTreeLeaf *leaf = verible::MaybeLeaf(&symbol);
+  if (leaf) return leaf->get().token_enum() == ',';
   return false;
 }
 
 static bool IsAnyPort(const verible::Symbol *symbol) {
-  if (symbol->Kind() == verible::SymbolKind::kNode) {
-    const auto *node = down_cast<const verible::SyntaxTreeNode *>(symbol);
+  const verible::SyntaxTreeNode *node = verible::MaybeNode(symbol);
+  if (node) {
     return node->MatchesTag(NodeEnum::kActualNamedPort) ||
            node->MatchesTag(NodeEnum::kActualPositionalPort);
   }
@@ -125,7 +123,7 @@
 
   verible::matcher::BoundSymbolManager manager;
   if (ParamsMatcher().Matches(symbol, &manager)) {
-    if (const auto *list = manager.GetAs<verible::SyntaxTreeNode>("list")) {
+    if (const verible::SyntaxTreeNode *list = manager.GetAsNode("list")) {
       const auto &children = list->children();
       auto parameter_count = std::count_if(
           children.begin(), children.end(),
@@ -159,8 +157,7 @@
   verible::matcher::BoundSymbolManager manager;
 
   if (InstanceMatcher().Matches(symbol, &manager)) {
-    if (const auto *port_list_node =
-            manager.GetAs<verible::SyntaxTreeNode>("list")) {
+    if (const auto *port_list_node = manager.GetAsNode("list")) {
       // Don't know how to handle unexpected non-portlist, so proceed
       if (!port_list_node->MatchesTag(NodeEnum::kPortActualList)) return;
 
diff --git a/verible/verilog/analysis/checkers/plusarg-assignment-rule.cc b/verible/verilog/analysis/checkers/plusarg-assignment-rule.cc
index bcae10b..cc027e6 100644
--- a/verible/verilog/analysis/checkers/plusarg-assignment-rule.cc
+++ b/verible/verilog/analysis/checkers/plusarg-assignment-rule.cc
@@ -61,7 +61,7 @@
     const verible::Symbol &symbol, const verible::SyntaxTreeContext &context) {
   verible::matcher::BoundSymbolManager manager;
   if (IdMatcher().Matches(symbol, &manager)) {
-    if (const auto *leaf = manager.GetAs<verible::SyntaxTreeLeaf>("name")) {
+    if (const verible::SyntaxTreeLeaf *leaf = manager.GetAsLeaf("name")) {
       if (kForbiddenFunctionName == leaf->get().text()) {
         violations_.insert(
             verible::LintViolation(leaf->get(), FormatReason(), context));
diff --git a/verible/verilog/analysis/checkers/truncated-numeric-literal-rule.cc b/verible/verilog/analysis/checkers/truncated-numeric-literal-rule.cc
index 8c24163..298bc6c 100644
--- a/verible/verilog/analysis/checkers/truncated-numeric-literal-rule.cc
+++ b/verible/verilog/analysis/checkers/truncated-numeric-literal-rule.cc
@@ -33,6 +33,7 @@
 #include "verible/common/text/symbol.h"
 #include "verible/common/text/syntax-tree-context.h"
 #include "verible/common/text/token-info.h"
+#include "verible/common/text/tree-utils.h"
 #include "verible/verilog/CST/numbers.h"
 #include "verible/verilog/CST/verilog-matchers.h"
 #include "verible/verilog/analysis/descriptions.h"
@@ -41,7 +42,6 @@
 namespace verilog {
 namespace analysis {
 
-using verible::down_cast;
 using verible::LintRuleStatus;
 using verible::LintViolation;
 using verible::SyntaxTreeContext;
@@ -152,18 +152,17 @@
     const verible::Symbol &symbol, const SyntaxTreeContext &context) {
   verible::matcher::BoundSymbolManager manager;
   if (!NumberMatcher().Matches(symbol, &manager)) return;
-  const auto *width_leaf = manager.GetAs<SyntaxTreeLeaf>("width");
-  const auto *literal_node = manager.GetAs<SyntaxTreeNode>("literal");
+  const SyntaxTreeLeaf *width_leaf = manager.GetAsLeaf("width");
+  const SyntaxTreeNode *literal_node = manager.GetAsNode("literal");
   if (!width_leaf || !literal_node) return;
 
-  const auto width_text = width_leaf->get().text();
+  const std::string_view width_text = width_leaf->get().text();
   size_t width;
   if (!absl::SimpleAtoi(width_text, &width)) return;
 
-  const auto *base_leaf =
-      down_cast<const SyntaxTreeLeaf *>((*literal_node)[0].get());
-  const auto *digits_leaf =
-      down_cast<const SyntaxTreeLeaf *>((*literal_node)[1].get());
+  const auto *base_leaf = verible::MaybeLeaf((*literal_node)[0].get());
+  const auto *digits_leaf = verible::MaybeLeaf((*literal_node)[1].get());
+  if (!base_leaf || !digits_leaf) return;
 
   const auto base_text = base_leaf->get().text();
   const auto digits_text = digits_leaf->get().text();
diff --git a/verible/verilog/analysis/checkers/undersized-binary-literal-rule.cc b/verible/verilog/analysis/checkers/undersized-binary-literal-rule.cc
index 2269fff..ffc7c67 100644
--- a/verible/verilog/analysis/checkers/undersized-binary-literal-rule.cc
+++ b/verible/verilog/analysis/checkers/undersized-binary-literal-rule.cc
@@ -33,6 +33,7 @@
 #include "verible/common/text/symbol.h"
 #include "verible/common/text/syntax-tree-context.h"
 #include "verible/common/text/token-info.h"
+#include "verible/common/text/tree-utils.h"
 #include "verible/common/util/logging.h"
 #include "verible/verilog/CST/numbers.h"
 #include "verible/verilog/CST/verilog-matchers.h"
@@ -43,12 +44,10 @@
 namespace analysis {
 
 using verible::AutoFix;
-using verible::down_cast;
 using verible::LintRuleStatus;
 using verible::LintViolation;
 using verible::SyntaxTreeContext;
 using verible::SyntaxTreeLeaf;
-using verible::SyntaxTreeNode;
 using verible::matcher::Matcher;
 
 // Register UndersizedBinaryLiteralRule
@@ -90,18 +89,19 @@
     const verible::Symbol &symbol, const SyntaxTreeContext &context) {
   verible::matcher::BoundSymbolManager manager;
   if (!NumberMatcher().Matches(symbol, &manager)) return;
-  const auto *width_leaf = manager.GetAs<SyntaxTreeLeaf>("width");
-  const auto *literal_node = manager.GetAs<SyntaxTreeNode>("literal");
+  const verible::SyntaxTreeLeaf *width_leaf = manager.GetAsLeaf("width");
+  const verible::SyntaxTreeNode *literal_node = manager.GetAsNode("literal");
   if (!width_leaf || !literal_node) return;
 
   const auto width_text = width_leaf->get().text();
   size_t width;
   if (!absl::SimpleAtoi(width_text, &width)) return;
 
-  const auto *base_leaf =
-      down_cast<const SyntaxTreeLeaf *>((*literal_node)[0].get());
-  const auto *digits_leaf =
-      down_cast<const SyntaxTreeLeaf *>((*literal_node)[1].get());
+  const SyntaxTreeLeaf *base_leaf =
+      verible::MaybeLeaf((*literal_node)[0].get());
+  const SyntaxTreeLeaf *digits_leaf =
+      verible::MaybeLeaf((*literal_node)[1].get());
+  if (!base_leaf || !digits_leaf) return;
 
   const auto base_text = base_leaf->get().text();
   const auto digits_text = digits_leaf->get().text();
diff --git a/verible/verilog/analysis/checkers/v2001-generate-begin-rule.cc b/verible/verilog/analysis/checkers/v2001-generate-begin-rule.cc
index ad7cd04..8d4b63b 100644
--- a/verible/verilog/analysis/checkers/v2001-generate-begin-rule.cc
+++ b/verible/verilog/analysis/checkers/v2001-generate-begin-rule.cc
@@ -63,7 +63,7 @@
     const verible::Symbol &symbol, const verible::SyntaxTreeContext &context) {
   verible::matcher::BoundSymbolManager manager;
   if (GenerateRegionMatcher().Matches(symbol, &manager)) {
-    if (const auto *block = manager.GetAs<verible::SyntaxTreeNode>("block")) {
+    if (const verible::SyntaxTreeNode *block = manager.GetAsNode("block")) {
       violations_.insert(LintViolation(verible::GetLeftmostLeaf(*block)->get(),
                                        kMessage, context));
     }
diff --git a/verible/verilog/analysis/checkers/void-cast-rule.cc b/verible/verilog/analysis/checkers/void-cast-rule.cc
index 724e682..9965d1d 100644
--- a/verible/verilog/analysis/checkers/void-cast-rule.cc
+++ b/verible/verilog/analysis/checkers/void-cast-rule.cc
@@ -38,8 +38,6 @@
 using verible::LintRuleStatus;
 using verible::LintViolation;
 using verible::SyntaxTreeContext;
-using verible::SyntaxTreeLeaf;
-using verible::SyntaxTreeNode;
 using verible::matcher::Matcher;
 
 // Register VoidCastRule
@@ -98,8 +96,7 @@
   // Check for forbidden function names
   verible::matcher::BoundSymbolManager manager;
   if (FunctionMatcher().Matches(symbol, &manager)) {
-    if (const auto *function_id =
-            manager.GetAs<verible::SyntaxTreeLeaf>("id")) {
+    if (const verible::SyntaxTreeLeaf *function_id = manager.GetAsLeaf("id")) {
       const auto &bfs = ForbiddenFunctionsSet();
       if (bfs.find(std::string(function_id->get().text())) != bfs.end()) {
         violations_.insert(LintViolation(function_id->get(),
@@ -111,8 +108,7 @@
   // Check for forbidden calls to randomize
   manager.Clear();
   if (RandomizeMatcher().Matches(symbol, &manager)) {
-    if (const auto *randomize_node =
-            manager.GetAs<verible::SyntaxTreeNode>("id")) {
+    if (const auto *randomize_node = manager.GetAsNode("id")) {
       const auto *leaf_ptr = verible::GetLeftmostLeaf(*randomize_node);
       const verible::TokenInfo token = ABSL_DIE_IF_NULL(leaf_ptr)->get();
       violations_.insert(LintViolation(
diff --git a/verible/verilog/formatting/BUILD b/verible/verilog/formatting/BUILD
index 0940ef8..929e592 100644
--- a/verible/verilog/formatting/BUILD
+++ b/verible/verilog/formatting/BUILD
@@ -30,7 +30,6 @@
         "//verible/common/text:token-info",
         "//verible/common/text:tree-context-visitor",
         "//verible/common/text:tree-utils",
-        "//verible/common/util:casts",
         "//verible/common/util:logging",
         "//verible/common/util:value-saver",
         "//verible/verilog/CST:context-functions",
diff --git a/verible/verilog/formatting/align.cc b/verible/verilog/formatting/align.cc
index de4cd89..55d8952 100644
--- a/verible/verilog/formatting/align.cc
+++ b/verible/verilog/formatting/align.cc
@@ -32,7 +32,6 @@
 #include "verible/common/text/token-info.h"
 #include "verible/common/text/tree-context-visitor.h"
 #include "verible/common/text/tree-utils.h"
-#include "verible/common/util/casts.h"
 #include "verible/common/util/logging.h"
 #include "verible/common/util/value-saver.h"
 #include "verible/verilog/CST/context-functions.h"
@@ -52,7 +51,6 @@
 using verible::AlignmentGroupAction;
 using verible::ByteOffsetSet;
 using verible::ColumnSchemaScanner;
-using verible::down_cast;
 using verible::ExtractAlignmentGroupsFunction;
 using verible::FormatTokenRange;
 using verible::PreFormatToken;
@@ -1551,7 +1549,7 @@
   const auto *origin = uwline.Origin();
   VLOG(2) << "origin is nullptr? " << (origin == nullptr);
   if (origin == nullptr) return;
-  const auto *node = down_cast<const SyntaxTreeNode *>(origin);
+  const SyntaxTreeNode *node = verible::MaybeNode(origin);
   VLOG(2) << "origin is node? " << (node != nullptr);
   if (node == nullptr) return;
   // Dispatch aligning function based on syntax tree node type.