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.

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.

Appendix: 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)