summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2019-05-18 23:59:04 +0000
committerTom Lane2019-05-18 23:59:04 +0000
commit4119af8f726646505834bd4cc05ff1925fae3046 (patch)
tree863fc467bcfdc20fba2947f9d79021ccb4afaec0
parent691ff7ca0ec314b9845934f5df006469d1e24172 (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.c2
-rw-r--r--indent.113
-rw-r--r--indent.c1
-rw-r--r--indent_globs.h2
-rw-r--r--lexi.c89
5 files changed, 98 insertions, 9 deletions
diff --git a/args.c b/args.c
index f79de75..3a0d8bf 100644
--- a/args.c
+++ b/args.c
@@ -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},
diff --git a/indent.1 b/indent.1
index 131abfe..a92adb4 100644
--- a/indent.1
+++ b/indent.1
@@ -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.
diff --git a/indent.c b/indent.c
index 9faf57a..4c9a5f7 100644
--- a/indent.c
+++ b/indent.c
@@ -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 */
diff --git a/lexi.c b/lexi.c
index 3c7bfef..7279fa0 100644
--- a/lexi.c
+++ b/lexi.c
@@ -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