Skip to content

pass_parallel.c emit_grpc_edge produces phantom Routes from non-gRPC var names #294

@sponger94

Description

@sponger94

Problem

pass_parallel.c emit_grpc_edge (around line 1216) emits Route nodes and GRPC_CALLS edges for any call site whose resolved QN looks gRPC-shaped, with two problems.

1. Wrong service name from greedy suffix-stripping

extract_grpc_service_method (line 1168) strips suffixes in this order:

static const char *suffixes[] = {"ServiceClient", "Client", "ServiceGrpc", "BlockingStub",
                                 "FutureStub",    "Stub",   "Servicer",    NULL};

ServiceClient is matched before Client. For a generated stub like FooServiceClient (Grpc.Tools convention), this strips to Foo — which does not match the .proto's declared service name FooService. The Route QN ends up __grpc__Foo/<rpc>, so cross-repo lookup against the consumer repo (which has the right name) misses.

2. False-positive Routes from local var names

When the resolved QN doesn't carry a generated-type suffix, the heuristic falls back to extracting the service name from the receiver var directly. In real codebases this picks up local fields like _provider, _builder, _someProvider, producing __grpc__provider/GetGroup, __grpc__builder/AddSomeService, etc. — Routes that look like gRPC services but correspond to no .proto anywhere. None match real services in target stores.

Suggested fixes

  1. Preserve the canonical service name. The proto-declared service name is <X>Service; generated client class is <X>ServiceClient. Suffix list should strip only Client, Stub, BlockingStub, FutureStub, AsyncStub, AsyncClient, Servicer — not ServiceClient or ServiceGrpc.

  2. Anchor on a stub-type signal, not a name shape. Only emit GRPC_CALLS when the receiver var has a typed assignment / ctor-param injection / DI registration that resolves to a known stub type — not when the resolved QN merely contains "Client" or "Stub" as a substring. The new pass_idl_scan introduced in feat: Tier 1 cross-repo gRPC matching + production-readiness fixes #293 takes this approach for its __route__grpc__ Routes (see Tier 1 detection rules) and includes a denylist for non-gRPC *Client types like System.Net.Http.HttpClient, RestSharp, Refit, Flurl, httpx. The same denylist + stub-suffix gate could be backported to pass_parallel.c directly.

  3. Optional: align Route QN format. pass_parallel uses __grpc__<service>/<method>; HTTP/async/pass_idl_scan Routes use __route__<protocol>__<id>. Aligning to __route__grpc__<service>/<method> would let producer-side and consumer-side Routes converge into a single namespace, regardless of which pass emitted them. pass_idl_scan already uses the __route__grpc__ namespace.

Status / interaction with #293

This issue is separate from and not blocked by #293. The PR ships the full Tier 1 stack (producer + consumer detection) plus the production-readiness fixes documented in .planning/tier1-extractor-fixes.md, but pass_parallel.c emit_grpc_edge is not touched. The new pass_idl_scan uses a separate Route QN namespace (__route__grpc__) and stricter detection rules with a denylist, so the two passes coexist — but pass_parallel's phantom __grpc__ Routes still pollute the graph alongside the new pass's correct output.

The collision-warning mitigation in #293 (logged at idl_scan.route_collision, with service_qn stored on Route props for a future FQN-aware matcher) is for the new pass's bare-key collisions across .proto packages. None of that addresses pass_parallel's issue, which is structural — the suffix list and var-name fallback in extract_grpc_service_method need to change.

Cross-references

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edges

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions