On Mon, Oct 10, 2022 at 10:42:42PM -0700, Junio C Hamano wrote: > >> - struct strvec argv = STRVEC_INIT; > >> + const char *argv[] = { "checkout", start_head.buf, > >> + "--", NULL }; > >> > >> - strvec_pushl(&argv, "checkout", start_head.buf, > >> - "--", NULL); > >> - if (run_command_v_opt(argv.v, RUN_GIT_CMD)) { > >> + if (run_command_v_opt(argv, RUN_GIT_CMD)) { > > > > This is OK with me, but note that one thing we lose is compiler > > protection that we remembered the trailing NULL pointer in the argv > > array (whereas strvec_pushl() has an attribute that makes sure the last > > argument is a NULL). > > The first parameter to run_command_v_opt() must be a NULL terminated > array of strings. argv.v[] after strvec_push*() is such a NULL > terminated array, and is suitable to be passed to the function. > > That much human programmers would know. > > But does the compiler know that run_command_v_opt() requires a NULL > terminated array of strings, and does it know to check that argv.v[] > came from strvec_pushl() without any annotation in the first place? No, but I don't think that's the interesting part. If you're using strvec, it does the right thing, and it's hard to get it wrong. I'm more concerned about places where we manually write a list of strings, and it's easy to forget the trailing NULL. In the existing code, that's done in the interface of strvec_pushl(), which will remind you if you write: strvec_pushl(&arg, "checkout", start_head.buf, "--"); But after it is done in an initializer, which has no clue about the expected semantics. We only have to get strvec's invariants right once. But every ad-hoc command argv has to remember the trailing NULL. > For such a check to happen, I think we need to tell the compiler > with some annotation that the first parameter to run_command_v_opt() > is supposed to be a NULL terminated char *[] array. Right, but I would not expect the compiler to realize that strvec maintains the ends-in-NULL invariant. It would have to be quite a clever compiler. In theory it could realize that argv is declared as an array locally, and could make sure it ends in NULL as a compile-time check. So it would have to be: "check this statically if you can, but otherwise assume it's OK" kind of warning. But it's all kind of moot since I don't think any such annotation exists. :) Possibly a linter like sparse could complain about declaring a variable called argv that doesn't end in NULL. I don't think it's worth anybody spending too much time on it, though. This hasn't historically been a big source of bugs. > > Probably not that big a deal in practice. It would be nice if there was > > a way to annotate this for the compiler, but I don't think there's any > > attribute for "this pointer-to-pointer parameter should have a NULL at > > the end". > > But the code before this patch is safe only for strvec_pushl() call, > not run_command_v_opt() call, so we are not losing anything, I would > think. The bug I'm worried about it is in a human writing the list of strings and forgetting the NULL, so there we are losing the (admittedly minor) protection. -Peff