user32: Don't print the function name twice in a FIXME.
[wine/multimedia.git] / tools / winapi / winapi_check
blob61a06351f87b7171ada810470c048b555a0a3886
1 #!/usr/bin/perl -w
3 # Copyright 1999-2002 Patrik Stridvall
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 # Note that winapi_check are using heuristics quite heavily.
21 # So always remember that:
23 # "Heuristics are bug ridden by definition.
24 # If they didn't have bugs, then they'd be algorithms."
26 # In other words, reported bugs are only potential bugs not
27 # real bugs, so they are called issues rather than bugs.
30 use strict;
32 BEGIN {
33 $0 =~ m%^(.*?/?tools)/winapi/winapi_check$%;
34 require "$1/winapi/setup.pm";
37 use config qw(
38 files_filter files_skip
39 get_h_files
40 $current_dir $wine_dir
42 use output qw($output);
43 use winapi_check_options qw($options);
45 BEGIN {
46 if($options->progress) {
47 $output->enable_progress;
48 } else {
49 $output->disable_progress;
53 use modules qw($modules);
54 use nativeapi qw($nativeapi);
55 use winapi qw($win16api $win32api @winapis);
57 use preprocessor;
58 use type;
59 use util qw(is_subset);
60 use winapi_documentation;
61 use winapi_function;
62 use winapi_local;
63 use winapi_global;
64 use winapi_parser;
66 my %include2info;
68 if ($options->global) {
69 my @files = get_h_files("winelib");
71 my $progress_current = 0;
72 my $progress_max = scalar(@files);
74 foreach my $file (@files) {
75 $progress_current++;
76 $output->lazy_progress("$file: file $progress_current of $progress_max");
78 my $file_dir = $file;
79 if(!($file_dir =~ s%(.*?)/[^/]+$%$1%)) {
80 $file_dir = ".";
83 $include2info{$file} = { name => $file };
85 open(IN, "< $wine_dir/$file") || die "Error: Can't open $wine_dir/$file: $!\n";
86 while(<IN>) {
87 if(/^\s*\#\s*include\s*\"(.*?)\"/) {
88 my $header = $1;
89 if(-e "$wine_dir/$file_dir/$header") {
90 $include2info{$file}{includes}{"$file_dir/$header"}++;
91 } elsif(-e "$wine_dir/$file_dir/../$header") {
92 if($file_dir =~ m%^(.*?)/[^/]+$%) {
93 $include2info{$file}{includes}{"$1/$header"}++;
94 } else {
95 $include2info{$file}{includes}{"$header"}++;
97 } elsif(-e "$wine_dir/include/$header") {
98 $include2info{$file}{includes}{"include/$header"}++;
99 } elsif ($header ne "config.h") {
100 $output->write("$file: #include \"$header\" is not a local include\n");
104 close(IN);
107 my @files2 = ("acconfig.h", "poppack.h", "pshpack1.h", "pshpack2.h", "pshpack4.h", "pshpack8.h",
108 "storage.h", "ver.h");
109 foreach my $file2 (@files2) {
110 $include2info{"include/$file2"}{used}++;
114 my @c_files = $options->c_files;
115 @c_files = files_skip(@c_files);
116 @c_files = files_filter("winelib", @c_files);
118 my @h_files = $options->h_files;
119 @h_files = files_skip(@h_files);
120 @h_files = files_filter("winelib", @h_files);
122 my $all_modules = 0;
123 my %complete_module;
124 if($options->global) {
125 my @complete_modules = $modules->complete_modules(\@c_files);
127 foreach my $module (@complete_modules) {
128 $complete_module{$module}++;
131 $all_modules = 1;
132 foreach my $module ($modules->all_modules) {
133 if(!$complete_module{$module}) {
134 $all_modules = 0;
135 if($wine_dir eq ".") {
136 $output->write("*.c: module $module is not complete\n");
142 if(1) {
143 foreach my $winapi (@winapis) {
144 foreach my $broken_forward ($winapi->all_broken_forwards) {
145 (my $module, my $external_name, my $forward_module, my $forward_external_name) = @$broken_forward;
146 if($complete_module{$forward_module}) {
147 $output->write("$module.spec: forward is broken: $external_name => $forward_module.$forward_external_name\n");
153 my $progress_current = 0;
154 my $progress_max = scalar(@c_files);
156 my %declared_functions;
158 if($options->headers) {
159 $progress_max += scalar(@h_files);
161 foreach my $file (@h_files) {
162 my %functions;
164 $progress_current++;
165 $output->progress("$file: file $progress_current of $progress_max");
167 my $found_c_comment = sub {
168 my $begin_line = shift;
169 my $end_line = shift;
170 my $comment = shift;
172 if(0) {
173 if($begin_line == $end_line) {
174 $output->write("$file:$begin_line: $comment\n");
175 } else {
176 $output->write("$file:$begin_line-$end_line: \\\n$comment\n");
181 my $found_cplusplus_comment = sub {
182 my $line = shift;
183 my $comment = shift;
185 if($options->comments_cplusplus) {
186 $output->write("$file:$line: C++ comments not allowed: $comment\n");
190 my $create_function = sub {
191 return 'winapi_function'->new;
194 my $found_function = sub {
195 my $function = shift;
197 my $internal_name = $function->internal_name;
199 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
200 $output->prefix_callback(sub { return $function->prefix; });
202 my $function_line = $function->function_line;
203 my $linkage = $function->linkage;
204 my $external_name = $function->external_name;
205 my $statements = $function->statements;
207 if($options->headers_misplaced &&
208 !($function->is_win16 && $function->is_win32) &&
209 (($function->is_win16 && $file =~ /^include\/[^\/]*$/) ||
210 ($function->is_win32 && $file =~ /^include\/wine\/[^\/]*$/)))
212 $output->write("declaration misplaced\n");
215 if(defined($external_name) && !defined($statements) &&
216 ($linkage eq "" || $linkage eq "extern"))
218 my $previous_function = $declared_functions{$internal_name};
219 if(!defined($previous_function)) {
220 $declared_functions{$internal_name} = $function;
221 } elsif($options->headers_duplicated) {
222 my $file = $previous_function->file;
223 my $function_line = $previous_function->function_line;
224 if($file =~ /\.h$/) {
225 $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
229 $output->prefix("");
232 my $create_type = sub {
233 return 'type'->new;
236 my $found_type = sub {
237 my $type = shift;
240 my $found_preprocessor = sub {
241 my $directive = shift;
242 my $argument = shift;
245 winapi_parser::parse_c_file($file, {
246 c_comment_found => $found_c_comment,
247 cplusplus_comment_found => $found_cplusplus_comment,
248 function_create => $create_function,
249 function_found => $found_function,
250 type_create => $create_type,
251 type_found => $found_type,
252 preprocessor_found => $found_preprocessor
257 my %module2functions = ();
259 foreach my $file (@c_files) {
260 my %functions = ();
261 my %includes = ();
263 $includes{$file}++;
265 my $file_module16 = $modules->allowed_modules_in_file("$current_dir/$file");
266 my $file_module32 = $modules->allowed_modules_in_file("$current_dir/$file");
268 $progress_current++;
269 $output->progress("$file (file $progress_current of $progress_max)");
271 my $file_dir = $file;
272 if(!($file_dir =~ s/(.*?)\/[^\/]*$/$1/)) {
273 $file_dir = ".";
276 my $found_c_comment = sub {
277 my $begin_line = shift;
278 my $end_line = shift;
279 my $comment = shift;
281 if(0) {
282 if($begin_line == $end_line) {
283 $output->write("$file:$begin_line: $comment\n");
284 } else {
285 $output->write("$file:$begin_line-$end_line: \\\n$comment\n");
290 my $found_cplusplus_comment = sub {
291 my $line = shift;
292 my $comment = shift;
294 if($options->comments_cplusplus) {
295 $output->write("$file:$line: C++ comments not allowed: $comment\n");
299 my $create_function = sub {
300 return 'winapi_function'->new;
303 my $found_function = sub {
304 my $function = shift;
306 my $internal_name = $function->internal_name;
307 $functions{$internal_name} = $function;
309 $output->progress("$file (file $progress_current of $progress_max): $internal_name");
310 $output->prefix_callback(sub { return $function->prefix; });
312 my $declared_function = $declared_functions{$internal_name};
314 my $documentation_line = $function->documentation_line;
315 my $documentation = $function->documentation;
316 my $linkage = $function->linkage;
317 my $return_type = $function->return_type;
318 my $calling_convention = $function->calling_convention;
319 my $statements = $function->statements;
321 my $module16 = $function->module16;
322 my $module32 = $function->module32;
324 my $external_name = $function->external_name;
325 my $external_name16 = $function->external_name16;
326 my $external_name32 = $function->external_name32;
328 if(defined($external_name) && !defined($statements) &&
329 ($linkage eq "" || $linkage eq "extern"))
331 my $previous_function = $declared_functions{$internal_name};
332 if(!defined($previous_function)) {
333 $declared_functions{$internal_name} = $function;
334 } else {
335 my $file = $previous_function->file;
336 my $function_line = $previous_function->function_line;
338 my $header = $file;
339 $header =~ s%^(include|$file_dir)/%%;
340 if($header !~ m%^msvcrt/% || $file_dir =~ m%^dlls/msvcrt%) {
341 $output->write("duplicate declaration (first declaration at $file:$function_line)\n");
346 if ($options->global) {
347 foreach my $module ($function->modules) {
348 $module2functions{$module}{$internal_name} = $function;
352 foreach my $module ($function->modules) {
353 $modules->found_module_in_dir($module, $file_dir);
356 if($options->shared) {
357 if($win16api->is_shared_internal_function($internal_name) ||
358 $win32api->is_shared_internal_function($internal_name))
360 $output->write("is shared between Win16 and Win32\n");
364 if($options->headers && $options->headers_needed &&
365 defined($declared_function) && defined($external_name) &&
366 defined($statements))
368 my $needed_include = $declared_function->file;
370 if(!defined($includes{$needed_include})) {
371 my $header = $needed_include;
372 $header =~ s%^(include|$file_dir)/%%;
373 if($header !~ m%^msvcrt/% || $file_dir =~ m%^dlls/msvcrt%) {
374 $output->write("prototype not included: #include \"$header\" is needed\n");
379 if($options->local && $options->argument && defined($statements)) {
380 winapi_local::check_function($function);
383 if($options->local && $options->statements && defined($statements)) {
384 winapi_local::check_statements(\%functions, $function);
387 if($options->local && $options->documentation &&
388 (defined($module16) || defined($module32)) &&
389 $linkage eq "" && defined($statements))
391 winapi_documentation::check_documentation($function);
394 if(1) {
395 # FIXME: Not correct
396 if(defined($external_name16)) {
397 $external_name16 = (split(/\s*&\s*/, $external_name16))[0];
400 # FIXME: Not correct
401 if(defined($external_name32)) {
402 $external_name32 = (split(/\s*&\s*/, $external_name32))[0];
405 if($options->local && $options->misplaced &&
406 $linkage ne "extern" && defined($statements))
408 if($options->win16 && $options->report_module($module16))
410 if($file ne "library/port.c" &&
411 !$nativeapi->is_function($internal_name) &&
412 !is_subset($module16, $file_module16))
414 foreach my $module16 (split(/\s*&\s*/, $module16)) {
415 if(!$win16api->is_function_stub($module16, $internal_name)) {
416 $output->write("is misplaced ($module16)\n");
422 if($options->win32 && $options->report_module($module32))
424 if($file ne "library/port.c" &&
425 !$nativeapi->is_function($internal_name) &&
426 !is_subset($module32, $file_module32))
428 foreach my $module32 (split(/\s*&\s*/, $module32)) {
429 if(!$win32api->is_function_stub($module32, $internal_name)) {
430 $output->write("is misplaced ($module32)\n");
437 if($options->local && $options->headers && $options->prototype) {
438 if($options->win16 && $options->report_module($module16)) {
439 if(!$nativeapi->is_function($internal_name) &&
440 !defined($declared_functions{$internal_name}))
442 $output->write("no prototype\n");
446 if($options->win32 && $options->report_module($module32)) {
447 if(!defined($external_name32) || (!$nativeapi->is_function($external_name32) && !defined($declared_functions{$external_name32})))
449 if(!defined($external_name32) || ($external_name32 !~ /^Dll(?:
450 Install|CanUnloadNow|GetClassObject|GetVersion|
451 RegisterServer|RegisterServerEx|UnregisterServer)|DriverProc$/x &&
452 $internal_name !~ /^COMCTL32_Str/ &&
453 $internal_name !~ /^(?:\Q$module32\E|wine)_(?:\Q$external_name32\E|\d+)$/))
455 $output->write("no prototype\n");
462 $output->prefix("");
465 my $config = 0;
466 my $conditional = 0;
467 my $found_include = sub {
468 local $_ = shift;
469 if(/^\"(?:config\.h|wine\/port\.h)\"/) {
470 $config++;
473 my $found_conditional = sub {
474 local $_ = shift;
476 $nativeapi->found_conditional($_);
478 if($options->config) {
479 if(!$nativeapi->is_conditional($_)) {
480 if(/^HAVE_/ && !/^HAVE_(?:IPX|CORRECT_LINUXINPUT_H|OSS|OSS_MIDI|V4L2)$/)
482 $output->write("$file: $_ is not declared as a conditional\n");
484 } else {
485 $conditional++;
486 if(!$config) {
487 $output->write("$file: conditional $_ used but config.h is not included\n");
493 my $create_type = sub {
494 return 'type'->new;
497 my $found_type = sub {
498 my $type = shift;
501 sub recursive_include {
502 my $include = shift;
503 my $includes = shift;
505 if(!defined($includes->{$include})) {
506 $includes->{$include}++;
507 foreach my $include (keys(%{$include2info{$include}{includes}})) {
508 recursive_include($include, \%$includes);
513 my $preprocessor = 'preprocessor'->new($found_include, $found_conditional);
514 my $found_preprocessor = sub {
515 my $directive = shift;
516 my $argument = shift;
518 $preprocessor->directive($directive, $argument);
520 if($options->config) {
521 if($directive eq "include") {
522 my $header;
523 my $check_protection;
524 my $check_local;
525 if($argument =~ /^<(.*?)>$/) {
526 $header = $1;
527 $check_protection = 1;
528 $check_local = 0;
529 } elsif($argument =~ /^\"(.*?)\"$/) {
530 $header = $1;
531 $check_protection = 0;
532 $check_local = 1;
533 } else {
534 $output->write("$file: #$directive $argument: is unparsable\n");
536 $header = undef;
537 $check_protection = 0;
538 $check_local = 0;
541 if(defined($header)) {
542 my $include;
543 if(-e "$wine_dir/include/$header") {
544 $include = "include/$header";
545 } elsif(-e "$wine_dir/include/msvcrt/$header") {
546 $include = "include/msvcrt/$header";
547 } elsif(-e "$file_dir/$header") {
548 $include = "$file_dir/$header";
549 } elsif(-e "$file_dir/../$header") {
550 if($file_dir =~ m%^(.*?)/[^/]+$%) {
551 $include = "$1/$header";
552 } else {
553 $include = "$header";
555 } elsif($check_local && $header ne "config.h") {
556 $output->write("$file: #include \"$header\": file not found\n");
559 if(defined($include)) {
560 recursive_include($include, \%includes);
564 if($check_protection && $header) {
565 if((-e "$wine_dir/include/$header" || -e "$wine_dir/$file_dir/$header")) {
566 if($header !~ /^(?:oleauto\.h|win(?:base|def|error|gdi|nls|nt|user)\.h)$/ &&
567 $file_dir !~ /tests$/)
569 $output->write("$file: #include \<$header\> is a local include\n");
573 my $macro = uc($header);
574 $macro =~ y/\.\//__/;
575 $macro = "HAVE_" . $macro;
577 if($nativeapi->is_conditional_header($header)) {
578 if(!$preprocessor->is_def($macro)) {
579 if($macro =~ /^HAVE_X11/) {
580 # Do nothing X Windows is handled differently
581 } elsif($macro =~ /^HAVE_(.*?)_H$/) {
582 my $name = $1;
583 if($header !~ /^alloca\.h$/ &&
584 $file_dir !~ /tests$/)
586 $output->write("$file: #$directive $argument: is a conditional include, " .
587 "but is not protected\n");
591 } elsif($preprocessor->is_def($macro)) {
592 $output->write("$file: #$directive $argument: is protected, but there is no check for it in configure.ac\n");
596 if($check_local && $header) {
597 if(-e "$file_dir/$header") {
598 if($file_dir ne ".") {
599 $include2info{"$file_dir/$header"}{used}++;
600 foreach my $name (keys(%{$include2info{"$file_dir/$header"}{includes}})) {
601 $include2info{$name}{used}++;
603 } else {
604 $include2info{"$header"}{used}++;
605 foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
606 $include2info{$name}{used}++;
609 } elsif(-e "$file_dir/../$header") {
610 if($file_dir =~ m%^(.*?)/[^/]+$%) {
611 $include2info{"$1/$header"}{used}++;
612 foreach my $name (keys(%{$include2info{"$1/$header"}{includes}})) {
613 $include2info{$name}{used}++;
615 } else {
616 $include2info{"$header"}{used}++;
617 foreach my $name (keys(%{$include2info{"$header"}{includes}})) {
618 $include2info{$name}{used}++;
621 } elsif(-e "$wine_dir/include/$header") {
622 $include2info{"include/$header"}{used}++;
623 foreach my $name (keys(%{$include2info{"include/$header"}{includes}})) {
624 $include2info{$name}{used}++;
626 } elsif(-e "$wine_dir/include/msvcrt/$header") {
627 $include2info{"include/msvcrt/$header"}{used}++;
628 foreach my $name (keys(%{$include2info{"include/msvcrt/$header"}{includes}})) {
629 $include2info{$name}{used}++;
631 } elsif ($header ne "config.h") {
632 $output->write("$file: #include \"$header\" is not a local include\n");
639 winapi_parser::parse_c_file($file, {
640 c_comment_found => $found_c_comment,
641 cplusplus_comment_found => $found_cplusplus_comment,
642 function_create => $create_function,
643 function_found => $found_function,
644 type_create => $create_type,
645 type_found => $found_type,
646 preprocessor_found => $found_preprocessor
649 if($options->config_unnecessary) {
650 if($config && $conditional == 0 && !exists($include2info{"include/wine/port.h"})) {
651 $output->write("$file: include2info config.h but do not use any conditionals\n");
655 winapi_local::check_file($file, \%functions);
658 if($options->global) {
659 winapi_global::check_modules(\%complete_module, \%module2functions);
661 if($all_modules) {
662 winapi_global::check_all_modules(\%include2info);