docbook: warn on unused doc entries

When you don't use !E or !I but only !F, then it's very easy to miss
including some functions, structs etc. in documentation. To help
finding which ones were missed, allow printing out the unused ones as
warnings.

For example, using this on mac80211 yields a lot of warnings like this:

Warning: didn't use docs for DOC: mac80211 workqueue
Warning: didn't use docs for ieee80211_max_queues
Warning: didn't use docs for ieee80211_bss_change
Warning: didn't use docs for ieee80211_bss_conf

when generating the documentation for it.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Johannes Berg and committed by
Linus Torvalds
eda603f6 1f3a6688

+183 -3
+5
Documentation/kernel-doc-nano-HOWTO.txt
··· 345 345 section titled <section title> from <filename>. 346 346 Spaces are allowed in <section title>; do not quote the <section title>. 347 347 348 + !C<filename> is replaced by nothing, but makes the tools check that 349 + all DOC: sections and documented functions, symbols, etc. are used. 350 + This makes sense to use when you use !F/!P only and want to verify 351 + that all documentation is included. 352 + 348 353 Tim. 349 354 */ <twaugh@redhat.com>
+128 -1
scripts/basic/docproc.c
··· 34 34 * 35 35 */ 36 36 37 + #define _GNU_SOURCE 37 38 #include <stdio.h> 38 39 #include <stdlib.h> 39 40 #include <string.h> 40 41 #include <ctype.h> 41 42 #include <unistd.h> 42 43 #include <limits.h> 44 + #include <errno.h> 43 45 #include <sys/types.h> 44 46 #include <sys/wait.h> 45 47 ··· 56 54 FILEONLY *internalfunctions; 57 55 FILEONLY *externalfunctions; 58 56 FILEONLY *symbolsonly; 57 + FILEONLY *findall; 59 58 60 59 typedef void FILELINE(char * file, char * line); 61 60 FILELINE * singlefunctions; ··· 68 65 #define KERNELDOCPATH "scripts/" 69 66 #define KERNELDOC "kernel-doc" 70 67 #define DOCBOOK "-docbook" 68 + #define LIST "-list" 71 69 #define FUNCTION "-function" 72 70 #define NOFUNCTION "-nofunction" 73 71 #define NODOCSECTIONS "-no-doc-sections" 74 72 75 73 static char *srctree, *kernsrctree; 74 + 75 + static char **all_list = NULL; 76 + static int all_list_len = 0; 77 + 78 + static void consume_symbol(const char *sym) 79 + { 80 + int i; 81 + 82 + for (i = 0; i < all_list_len; i++) { 83 + if (!all_list[i]) 84 + continue; 85 + if (strcmp(sym, all_list[i])) 86 + continue; 87 + all_list[i] = NULL; 88 + break; 89 + } 90 + } 76 91 77 92 static void usage (void) 78 93 { ··· 269 248 struct symfile * sym = &symfilelist[i]; 270 249 for (j=0; j < sym->symbolcnt; j++) { 271 250 vec[idx++] = type; 251 + consume_symbol(sym->symbollist[j].name); 272 252 vec[idx++] = sym->symbollist[j].name; 273 253 } 274 254 } ··· 309 287 vec[idx++] = &line[i]; 310 288 } 311 289 } 290 + for (i = 0; i < idx; i++) { 291 + if (strcmp(vec[i], FUNCTION)) 292 + continue; 293 + consume_symbol(vec[i + 1]); 294 + } 312 295 vec[idx++] = filename; 313 296 vec[idx] = NULL; 314 297 exec_kernel_doc(vec); ··· 333 306 if (*s == '\n') 334 307 *s = '\0'; 335 308 309 + asprintf(&s, "DOC: %s", line); 310 + consume_symbol(s); 311 + free(s); 312 + 336 313 vec[0] = KERNELDOC; 337 314 vec[1] = DOCBOOK; 338 315 vec[2] = FUNCTION; ··· 346 315 exec_kernel_doc(vec); 347 316 } 348 317 318 + static void find_all_symbols(char *filename) 319 + { 320 + char *vec[4]; /* kerneldoc -list file NULL */ 321 + pid_t pid; 322 + int ret, i, count, start; 323 + char real_filename[PATH_MAX + 1]; 324 + int pipefd[2]; 325 + char *data, *str; 326 + size_t data_len = 0; 327 + 328 + vec[0] = KERNELDOC; 329 + vec[1] = LIST; 330 + vec[2] = filename; 331 + vec[3] = NULL; 332 + 333 + if (pipe(pipefd)) { 334 + perror("pipe"); 335 + exit(1); 336 + } 337 + 338 + switch (pid=fork()) { 339 + case -1: 340 + perror("fork"); 341 + exit(1); 342 + case 0: 343 + close(pipefd[0]); 344 + dup2(pipefd[1], 1); 345 + memset(real_filename, 0, sizeof(real_filename)); 346 + strncat(real_filename, kernsrctree, PATH_MAX); 347 + strncat(real_filename, "/" KERNELDOCPATH KERNELDOC, 348 + PATH_MAX - strlen(real_filename)); 349 + execvp(real_filename, vec); 350 + fprintf(stderr, "exec "); 351 + perror(real_filename); 352 + exit(1); 353 + default: 354 + close(pipefd[1]); 355 + data = malloc(4096); 356 + do { 357 + while ((ret = read(pipefd[0], 358 + data + data_len, 359 + 4096)) > 0) { 360 + data_len += ret; 361 + data = realloc(data, data_len + 4096); 362 + } 363 + } while (ret == -EAGAIN); 364 + if (ret != 0) { 365 + perror("read"); 366 + exit(1); 367 + } 368 + waitpid(pid, &ret ,0); 369 + } 370 + if (WIFEXITED(ret)) 371 + exitstatus |= WEXITSTATUS(ret); 372 + else 373 + exitstatus = 0xff; 374 + 375 + count = 0; 376 + /* poor man's strtok, but with counting */ 377 + for (i = 0; i < data_len; i++) { 378 + if (data[i] == '\n') { 379 + count++; 380 + data[i] = '\0'; 381 + } 382 + } 383 + start = all_list_len; 384 + all_list_len += count; 385 + all_list = realloc(all_list, sizeof(char *) * all_list_len); 386 + str = data; 387 + for (i = 0; i < data_len && start != all_list_len; i++) { 388 + if (data[i] == '\0') { 389 + all_list[start] = str; 390 + str = data + i + 1; 391 + start++; 392 + } 393 + } 394 + } 395 + 349 396 /* 350 397 * Parse file, calling action specific functions for: 351 398 * 1) Lines containing !E ··· 431 322 * 3) Lines containing !D 432 323 * 4) Lines containing !F 433 324 * 5) Lines containing !P 434 - * 6) Default lines - lines not matching the above 325 + * 6) Lines containing !C 326 + * 7) Default lines - lines not matching the above 435 327 */ 436 328 static void parse_file(FILE *infile) 437 329 { ··· 475 365 s++; 476 366 docsection(line + 2, s); 477 367 break; 368 + case 'C': 369 + while (*s && !isspace(*s)) s++; 370 + *s = '\0'; 371 + if (findall) 372 + findall(line+2); 373 + break; 478 374 default: 479 375 defaultline(line); 480 376 } ··· 496 380 int main(int argc, char *argv[]) 497 381 { 498 382 FILE * infile; 383 + int i; 499 384 500 385 srctree = getenv("SRCTREE"); 501 386 if (!srctree) ··· 532 415 symbolsonly = find_export_symbols; 533 416 singlefunctions = noaction2; 534 417 docsection = noaction2; 418 + findall = find_all_symbols; 535 419 parse_file(infile); 536 420 537 421 /* Rewind to start from beginning of file again */ ··· 543 425 symbolsonly = printline; 544 426 singlefunctions = singfunc; 545 427 docsection = docsect; 428 + findall = NULL; 546 429 547 430 parse_file(infile); 431 + 432 + for (i = 0; i < all_list_len; i++) { 433 + if (!all_list[i]) 434 + continue; 435 + fprintf(stderr, "Warning: didn't use docs for %s\n", 436 + all_list[i]); 437 + } 548 438 } 549 439 else if (strcmp("depend", argv[1]) == 0) 550 440 { ··· 565 439 symbolsonly = adddep; 566 440 singlefunctions = adddep2; 567 441 docsection = adddep2; 442 + findall = adddep; 568 443 parse_file(infile); 569 444 printf("\n"); 570 445 }
+50 -2
scripts/kernel-doc
··· 44 44 # Note: This only supports 'c'. 45 45 46 46 # usage: 47 - # kernel-doc [ -docbook | -html | -text | -man ] [ -no-doc-sections ] 47 + # kernel-doc [ -docbook | -html | -text | -man | -list ] [ -no-doc-sections ] 48 48 # [ -function funcname [ -function funcname ...] ] c file(s)s > outputfile 49 49 # or 50 50 # [ -nofunction funcname [ -function funcname ...] ] c file(s)s > outputfile 51 51 # 52 52 # Set output format using one of -docbook -html -text or -man. Default is man. 53 + # The -list format is for internal use by docproc. 53 54 # 54 55 # -no-doc-sections 55 56 # Do not output DOC: sections ··· 211 210 $type_param, "\$1" ); 212 211 my $blankline_text = ""; 213 212 213 + # list mode 214 + my %highlights_list = ( $type_constant, "\$1", 215 + $type_func, "\$1", 216 + $type_struct, "\$1", 217 + $type_param, "\$1" ); 218 + my $blankline_list = ""; 214 219 215 220 sub usage { 216 - print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man ] [ -no-doc-sections ]\n"; 221 + print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man | -list ]\n"; 222 + print " [ -no-doc-sections ]\n"; 217 223 print " [ -function funcname [ -function funcname ...] ]\n"; 218 224 print " [ -nofunction funcname [ -nofunction funcname ...] ]\n"; 219 225 print " c source file(s) > outputfile\n"; ··· 326 318 $output_mode = "xml"; 327 319 %highlights = %highlights_xml; 328 320 $blankline = $blankline_xml; 321 + } elsif ($cmd eq "-list") { 322 + $output_mode = "list"; 323 + %highlights = %highlights_list; 324 + $blankline = $blankline_list; 329 325 } elsif ($cmd eq "-gnome") { 330 326 $output_mode = "gnome"; 331 327 %highlights = %highlights_gnome; ··· 1370 1358 print " $section:\n"; 1371 1359 print " -> "; 1372 1360 output_highlight($args{'sections'}{$section}); 1361 + } 1362 + } 1363 + 1364 + ## list mode output functions 1365 + 1366 + sub output_function_list(%) { 1367 + my %args = %{$_[0]}; 1368 + 1369 + print $args{'function'} . "\n"; 1370 + } 1371 + 1372 + # output enum in list 1373 + sub output_enum_list(%) { 1374 + my %args = %{$_[0]}; 1375 + print $args{'enum'} . "\n"; 1376 + } 1377 + 1378 + # output typedef in list 1379 + sub output_typedef_list(%) { 1380 + my %args = %{$_[0]}; 1381 + print $args{'typedef'} . "\n"; 1382 + } 1383 + 1384 + # output struct as list 1385 + sub output_struct_list(%) { 1386 + my %args = %{$_[0]}; 1387 + 1388 + print $args{'struct'} . "\n"; 1389 + } 1390 + 1391 + sub output_blockhead_list(%) { 1392 + my %args = %{$_[0]}; 1393 + my ($parameter, $section); 1394 + 1395 + foreach $section (@{$args{'sectionlist'}}) { 1396 + print "DOC: $section\n"; 1373 1397 } 1374 1398 } 1375 1399