diff options
author | Tom Lane | 2019-05-18 23:59:04 +0000 |
---|---|---|
committer | Tom Lane | 2019-05-18 23:59:04 +0000 |
commit | 4119af8f726646505834bd4cc05ff1925fae3046 (patch) | |
tree | 863fc467bcfdc20fba2947f9d79021ccb4afaec0 | |
parent | 691ff7ca0ec314b9845934f5df006469d1e24172 (diff) |
Improve indent's ability to distinguish function defs from decls.
The previous coding for deciding whether "foo(" begins a function
definition or just a declaration was quite stupid: it could be
fooled by nested parentheses in the parameter list, or comments
in or after the parameter list, or whitespace just after the
parameter list. And it didn't work if the parameter list crossed
a line boundary, either.
We can upgrade all those cases without any objectionable side
effects, I believe. However, there's one more case that's
problematic: if there's non-punctuation stuff after the
parenthesized arguments, it's difficult to tell whether that
stuff is K&R-style parameter declarations or function attributes.
I don't care to try to make the lookahead code smart enough to
tell the difference, so instead let's invent a switch to tell
what to do. The '-kr' mode is backward-compatible, hence the
default. The '-nkr' mode will do the right thing with function
attributes and the wrong thing with pre-ANSI function declarations;
but for a code base with none of the latter, that hardly matters.
-rw-r--r-- | args.c | 2 | ||||
-rw-r--r-- | indent.1 | 13 | ||||
-rw-r--r-- | indent.c | 1 | ||||
-rw-r--r-- | indent_globs.h | 2 | ||||
-rw-r--r-- | lexi.c | 89 |
5 files changed, 98 insertions, 9 deletions
@@ -125,6 +125,7 @@ struct pro { {"fcb", PRO_BOOL, true, ON, &format_block_comments}, {"ip", PRO_BOOL, true, ON, &ps.indent_parameters}, {"i", PRO_INT, 8, 0, &ps.ind_size}, + {"kr", PRO_BOOL, true, ON, &knr_function_style}, {"lc", PRO_INT, 0, 0, &block_comment_max_col}, {"ldi", PRO_INT, -1, 0, &ps.local_decl_indent}, {"lpl", PRO_BOOL, false, ON, &lineup_to_parens_always}, @@ -146,6 +147,7 @@ struct pro { {"nfc1", PRO_BOOL, true, OFF, &format_col1_comments}, {"nfcb", PRO_BOOL, true, OFF, &format_block_comments}, {"nip", PRO_BOOL, true, OFF, &ps.indent_parameters}, + {"nkr", PRO_BOOL, true, OFF, &knr_function_style}, {"nlpl", PRO_BOOL, false, OFF, &lineup_to_parens_always}, {"nlp", PRO_BOOL, true, OFF, &lineup_to_parens}, {"npcs", PRO_BOOL, false, OFF, &proc_calls_space}, @@ -70,6 +70,7 @@ .Ek .Op Fl i Ns Ar n .Op Fl \&ip | Fl nip +.Op Fl kr | Fl nkr .Op Fl l Ns Ar n .Op Fl \&lc Ns Ar n .Op Fl \&ldi Ns Ar n @@ -345,6 +346,18 @@ Enables (disables) the indentation of parameter declarations from the left margin. The default is .Fl \&ip . +.It Fl kr , nkr +If +.Fl kr +is specified, assume text following a possible function declaration +is parameter declaration(s) as in Kernighan-and-Ritchie C; that is, +the presence of such text means it's a function definition. +If +.Fl nkr +is specified, assume such text is an attribute declaration, +as in modern C. +Default: +.Fl kr . .It Fl l Ns Ar n Maximum length of an output line. The default is 78. @@ -173,6 +173,7 @@ main(int argc, char **argv) ps.case_indent = 0; /* -cli0 */ format_block_comments = 1; /* -fcb */ format_col1_comments = 1; /* -fc1 */ + knr_function_style = 1; /* -kr */ procnames_start_line = 1; /* -psl */ proc_calls_space = 0; /* -npcs */ comment_delimiter_on_blankline = 1; /* -cdb */ diff --git a/indent_globs.h b/indent_globs.h index d018af1..9feb362 100644 --- a/indent_globs.h +++ b/indent_globs.h @@ -219,6 +219,8 @@ int use_tabs; /* set true to use tabs for spacing, * false uses all spaces */ int auto_typedefs; /* set true to recognize identifiers * ending in "_t" like typedefs */ +int knr_function_style; /* assume stuff after int foo(...) + * is K&R-style param declarations */ int space_after_cast; /* "b = (int) a" vs "b = (int)a" */ int postgres_tab_rules; /* use Postgres tab-vs-space rules */ int tabsize; /* the size of a tab */ @@ -148,6 +148,80 @@ strcmp_type(const void *e1, const void *e2) return (strcmp(e1, *(const char * const *)e2)); } +/* + * Decide whether "foo(..." is a function definition or declaration. + * At call, we are looking at the '('. Look ahead past the argument(s) + * to the matching ')'. Then: + * + * In knr_function_style mode, if the next non-whitespace character + * is ';' or ',', it's a declaration; otherwise it's a definition. + * (This mode is fooled by function attributes, which look too much + * like a K&R-style parameter declaration.) + * + * In non-K&R mode, keep scanning until we find '{', ';' or ',' not + * within parentheses; then it's a definition if we found '{', otherwise a + * declaration. (This mode is fooled by K&R-style parameter declarations.) + * + * We ignore whitespace and comments in either mode. (The current + * coding does not handle // comments, but that could be fixed.) + */ +static int +is_func_definition(char *tp) +{ + int paren_depth = 0; + int in_comment = false; + int lastc = 0; + + /* We may need to look past the end of the current buffer. */ + lookahead_reset(); + for (;;) { + int c; + + /* Fetch next character. */ + if (tp < buf_end) + c = *tp++; + else { + c = lookahead(); + if (c == EOF) + break; + } + /* Handle comments. */ + if (in_comment) { + if (lastc == '*' && c == '/') + in_comment = false; + } else if (lastc == '/' && c == '*') + in_comment = true; + /* Count nested parens properly. */ + else if (c == '(') + paren_depth++; + else if (c == ')') { + paren_depth--; + /* + * If we find unbalanced parens, we must have started inside a + * declaration. + */ + if (paren_depth < 0) + return false; + } else if (paren_depth == 0) { + /* We are outside any parentheses or comments. */ + if (knr_function_style) { + /* First nonblank determines it. */ + if (!isspace((unsigned char) c)) + return !(c == ';' || c == ','); + } else { + /* In this mode, we scan till we find definitive punctuation. */ + if (c == '{') + return true; + else if (c == ';' || c == ',') + return false; + } + } + lastc = c; + } + /* Hit EOF --- for lack of anything better, assume "not a definition". */ + return false; +} + int lexi(struct parser_state *state) { @@ -348,15 +422,12 @@ lexi(struct parser_state *state) } /* end of if (found_it) */ if (*buf_ptr == '(' && state->tos <= 1 && state->ind_level == 0 && state->in_parameter_declaration == 0 && state->block_init == 0) { - char *tp = buf_ptr; - while (tp < buf_end) - if (*tp++ == ')' && (*tp == ';' || *tp == ',')) - goto not_proc; - strncpy(state->procname, token, sizeof state->procname - 1); - if (state->in_decl) - state->in_parameter_declaration = 1; - return (funcname); - not_proc:; + if (is_func_definition(buf_ptr)) { + strncpy(state->procname, token, sizeof state->procname - 1); + if (state->in_decl) + state->in_parameter_declaration = 1; + return (funcname); + } } /* * The following hack attempts to guess whether or not the current |