antlr/antlr4 · error · ArgumentException

patternTree cannot be null

Error message

patternTree cannot be null

What it means

ParseTreePatternMatcher.MatchImpl rejects a null patternTree argument. The pattern tree is the compiled form of the pattern string produced by ParseTreePatternMatcher.Compile; the recursion always supplies it, so a null means MatchImpl was invoked directly or from an override that lost the pattern tree.

Source

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

        /// 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;
                        // track label->list-of-nodes for both token name and label (if any)

                        labels.Map(tokenTagToken.TokenName, tree);
                        if (tokenTagToken.Label != null)

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Forward the patternTree parameter that MatchImpl receives when recursing into children.
  2. Ensure pattern trees come from ParseTreePatternMatcher.Compile(...) / parser.CompileParseTreePattern(...) and cache misses are compiled, not skipped.
  3. Use the public match API instead of MatchImpl.

Example fix

// before
base.MatchImpl(tree.GetChild(i), cachedPattern /* null on miss */, labels);

// after
if (!cache.TryGetValue(key, out var ptree)) { ptree = Compile(key); cache[key] = ptree; }
base.MatchImpl(tree.GetChild(i), ptree, labels);
Defensive patterns

Strategy: validation

Validate before calling

var ptree = patternTree ?? throw new InvalidOperationException("pattern tree not compiled");
var r = MatchImpl(tree, ptree, labels);

Type guard

static bool HasPatternTree(IParseTree p) => p != null;

Prevention

When it happens

Trigger: Calling MatchImpl(tree, null, labels) directly; an override that stores the pattern tree in a field initialized to null instead of forwarding the parameter.

Common situations: Custom matcher subclasses that restructure the recursion; caching pattern trees in a dictionary that returns null on a miss and forwarding the miss; refactors between Java and C# runtimes dropping an argument.

Related errors


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