The current funcname matcher for C files requires one or
more words before the function name, like:

  static int foo(int arg)

However, some coding styles look like this:

  static int
  foo(int arg)

and we do not match, even though the default regex would.

This patch simplifies the regex significantly. Rather than
trying to handle all the variants of keywords and return
types, we simply look for an identifier at the start of the
line that contains a "(", meaning it is either a function
definition or a function call, and then not containing ";"
which would indicate it is a call or declaration.

This can be fooled by something like:

    if (foo)

but it is unlikely to find that at the very start of a line
with no identation (and without having a complete set of
keywords, we cannot distinguish that from a function called
"if" taking "foo" anyway).

We had no tests at all for .c files, so this attempts to add
a few obvious cases (including the one we are fixing here).

Signed-off-by: Jeff King <>
I tried accommodating this one case in the current regex, but it just
kept getting more complicated and unreadable. Maybe I am being naive to
think that this much simpler version can work. We don't have a lot of
tests or a known-good data set to check.

I did try "git log -1000 -p" before and after on git.git, diff'd the
results and manually inspected. The results were a mix of strict
improvement (mostly instances of the style I was trying to fix here) and
cases where there is no good answer. For example, for top-level changes
outside functions, we might find:

  N_("some text that is long"

that is part of:

  const char *foo =
  N_("some text that is long"
  "and spans multiple lines");

Before this change, we would skip past it (using the cpp regex, that is;
the default one tends to find the same line) and either report nothing,
or whatever random function was before us. So it's a behavior change,
but the existing behavior is really no better.

 t/ | 36 ++++++++++++++++++++++++++++++++++++
 userdiff.c               |  2 +-
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/t/ b/t/
index 38a092a..1e80b15 100755
--- a/t/
+++ b/t/
@@ -93,6 +93,29 @@ sed -e '
 ' <Beer.perl >Beer-correct.perl
+cat >Beer.c <<\EOF
+static int foo(void)
+       int x = old;
+struct foo; /* catch failure below */
+static int
+gnu(int arg)
+       int x = old;
+struct foo; /* catch failure below */
+int multiline(int arg,
+             char *arg2)
+       int x = old;
+sed s/old/new/ <Beer.c >Beer-correct.c
 test_expect_funcname () {
        test_expect_code 1 git diff --no-index -U1 \
@@ -127,6 +150,7 @@ test_expect_success 'set up .gitattributes declaring 
drivers to test' '
        cat >.gitattributes <<-\EOF
        *.java diff=java
        *.perl diff=perl
+       *.c diff=cpp
@@ -158,6 +182,18 @@ test_expect_success 'perl pattern is not distracted by 
forward declaration' '
        test_expect_funcname "package Beer;\$" perl
+test_expect_success 'c pattern skips labels' '
+       test_expect_funcname "static int foo(void)" c
+test_expect_success 'c pattern matches GNU-style functions' '
+       test_expect_funcname "gnu(int arg)\$" c
+test_expect_success 'c pattern matches multiline functions' '
+       test_expect_funcname "int multiline(int arg,\$" c
 test_expect_success 'custom pattern' '
        test_config "!static
diff --git a/userdiff.c b/userdiff.c
index 10b61ec..b9d52b7 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -127,7 +127,7 @@
         /* Jump targets or access declarations */
         "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
         /* C/++ functions/methods at top level */
-        "^([A-Za-z_][A-Za-z_0-9]*([ \t*]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ 
\t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
+        "^([A-Za-z_].*\\([^;]*)$\n"
         /* compound type at top level */
         /* -- */
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to
More majordomo info at

Reply via email to