antlr/antlr4 · error · ArgumentException

tree cannot be null

Error message

tree cannot be null

What it means

ParseTreePatternMatcher.MatchImpl rejects a null tree argument. MatchImpl is the protected recursion engine behind ParseTreePattern.Match(); every level of the recursion passes subtree children in, and a null there indicates a structural bug (a child accessor returned null) or a custom override calling it with null. The public API never passes null from a real parse tree.

Source

Thrown at runtime/CSharp/src/Tree/Pattern/ParseTreePatternMatcher.cs:395

        /// </summary>
        /// <returns>
        /// the first node encountered in
        /// <paramref name="tree"/>
        /// which does not match
        /// a corresponding node in
        /// <paramref name="patternTree"/>
        /// , or
        /// <see langword="null"/>
        /// if the match
        /// was successful. The specific node returned depends on the matching
        /// algorithm used by the implementation, and may be overridden.
        /// </returns>
        [return: Nullable]
        protected internal virtual IParseTree MatchImpl(IParseTree tree, IParseTree patternTree, MultiMap<string, IParseTree> labels)
        {
            if (tree == null)
            {
                throw new ArgumentException("tree cannot be null");
            }
            if (patternTree == null)
            {
                throw new ArgumentException("patternTree cannot be null");
            }
            // x and <ID>, x and y, or x and x; or could be mismatched types
            if (tree is ITerminalNode && patternTree is ITerminalNode)
            {
                ITerminalNode t1 = (ITerminalNode)tree;
                ITerminalNode t2 = (ITerminalNode)patternTree;
                IParseTree mismatchedNode = null;
                // both are tokens and they have same type
                if (t1.Symbol.Type == t2.Symbol.Type)
                {
                    if (t2.Symbol is TokenTagToken)
                    {
                        // x and <ID>
                        TokenTagToken tokenTagToken = (TokenTagToken)t2.Symbol;

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Do not call MatchImpl yourself; use tree.Pattern(p).Match() / p.Match(tree).
  2. In overrides, guard children before recursing: skip null children or treat them as mismatches instead of passing them down.
  3. Fix custom IParseTree implementations so GetChild(i) never returns null for i < ChildCount.

Example fix

// before
protected override IParseTree MatchImpl(IParseTree tree, IParseTree patternTree, MultiMap<string, IParseTree> labels)
{
    return base.MatchImpl(GetChildOrNull(tree, 0), patternTree, labels);
}

// after
var child = GetChildOrNull(tree, 0);
return child == null ? tree /* mismatch */ : base.MatchImpl(child, patternTree, labels);
Defensive patterns

Strategy: validation

Validate before calling

// In overrides, guard children before recursing
for (int i = 0; i < tree.ChildCount; i++)
{
    var child = tree.GetChild(i);
    if (child == null) continue; // treat as mismatch instead of throwing
    var r = MatchImpl(child, patternTree.GetChild(i), labels);
}

Type guard

static bool HasTree(IParseTree t) => t != null;

Prevention

When it happens

Trigger: Overriding or calling MatchImpl(null, patternTree, labels) directly; a custom IParseTree implementation whose GetChild() returns null for valid indexes, which the recursion then feeds into MatchImpl.

Common situations: Subclassing the pattern matcher to customize matching; wrapping/altering parse trees with custom node types that violate the IParseTree contract; porting code from an older runtime where null children were tolerated.

Related errors


AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14). Data as JSON: /api/errors/7e7fba89fba449f0. Report an issue: GitHub.