style.test 10.9 KB
Newer Older
1
#! /bin/sh
2
# -*- coding: utf-8 -*-
3
4
# Copyright (C) 2009-2017 Laboratoire de Recherche et Développement de
# l'Epita (LRDE).
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6
# (LIP6), département Systèmes Répartis Coopératifs (SRC), Université
# Pierre et Marie Curie.
#
# This file is part of Spot, a model checking library.
#
# Spot is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Spot is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
24
25
26

# Ensure consistent style by catching common improper constructs.

set -e
27
set +x
28
29
30
31
32
33
34
35

diag()
{
  fail=:
  echo "$file:" "$@"
  echo ============================================================
}

36
rm -f failures.style
37

38
39
GREP=grep

40
# Get some help from GNU grep.
41
if (grep --color=auto -n --version)>/dev/null 2>&1; then
42
  GREP="$GREP --color=auto -n"
43
44
45
  GREP_COLOR='1;31'
  export GREP_COLOR
fi
46

47
# Reset the locale, so for instance we don't get bugs because sed is
48
# expecting utf-8 and we feed it latin-1.  The C locale should be OK,
49
50
51
52
53
# because we do not treat extended characters specifically in the
# following style rules.
LC_ALL=C
export LC_ALL

54
55
mkdir -p style.dir
tmp=style.dir/incltest.tmp.$$
56

57
58
59
# We used to loop over more directories before the source tree was
# rearranged.  So there is only one left today, but we keep the loop
# in case we want to add more in the future.
60
61
TOP=${srcdir-.}/../..
for dir in "$TOP/spot" "$TOP/bin" "$TOP/tests"; do
62

63
64
  find "$dir" \(    -name "${1-*}.hh" \
                 -o -name "${1-*}.hxx" \
65
66
                 -o -name "${1-*}.cc" \
                 -o -name "${1-*}.test" \) \
67
68
                 -a -not -path '*.dir/*' \
                 -a -type f -a -print |
69
  while read file; do
70

71
72
    if $GREP 'GNU Bison' "$file" >/dev/null ||
      $GREP 'generated by flex' "$file" >/dev/null ; then
73
      continue
74
    fi
75

76
77
78
79
80
    # Skip the files used by sanity.
    case $file in
	*incltest.cc) continue;;
    esac

81
    fail=false
82

83
    # Check this before stripping comments and strings.
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
84
85
86
87
88
    $GREP -i 'accepting cond' $file && diag 'accepting -> acceptance'
    $GREP -i 'dictionnar[yi]' $file && diag 'dictionnary -> dictionary'
    # "an uninstalled" seems to be the exception so far, but we want
    # "a unique", "a universal", etc.
    $GREP -i 'an uni[^n]' $file && diag 'an uni... -> a uni...'
89

90
    $GREP -i 'version 2 of the License' $file &&
91
      diag 'license text should refer to version 2'
92
    $GREP -i 'Temple Place' $file &&
93
94
      diag 'license text should give a url instead of an address'

95
96
97
    $GREP -q 'coding: utf-8' $file ||
      diag 'missing -*- coding: utf-8 -*-'

98
    $GREP Copyright $file >/dev/null ||
99
100
      diag "missing copyright"

101
    # If some grep implementation ignores LC_ALL=C, this rule might be
102
103
104
    # a problem on utf-8 characters such as "δ" which really
    # corresponds to multiple bytes, but might be matched as a single
    # character by grep.
105
    $GREP '<< *"\([^\\]\|\\.\)"' $file &&
106
107
      diag "Use << 'c' instead" 'of << "c".'

108
109
110
111
112
113
114
115
116
117
118
119
    # A doxygen comments such as
    #
    # | \brief foo
    # | \ingroup bar
    # |
    # | baz
    #
    # will be output as "foobaz."  But if the first two lines
    # are reversed, it's output correctly.
    perl -ne '/(.*\\brief.*\n.*\\ingroup.*)/ && print("$1\n") && exit(1)' \
      -0777 $file || diag "always put 'ingroup' before 'brief'"

120
121
122
123
124
125
126
    # Check this before we remove SPOT_API from the input.
    case $file in
	*.cc)
	    $GREP 'SPOT_API' $file &&
		diag 'use SPOT_API only in header files';;
    esac

127
    # Strip comments and strings.
128
129
130
131
132
133
134
135
136
137
    #
    # Multi-line comments of the form
    # /* Line 1
    #    Line 2
    #    Line 3 */
    # are replaced by
    # //
    # //
    # //
    # to keep the line numbers correct in the diagnostics.
138
    #
139
    # Also get rid of the SPOT_API tags.
140
141
142
    perl -pe 'sub f {my $a = shift; $a =~ s:[^\n]*://:g; return "$a"}
              s,/\*(.*?)\*/,f($1),sge;
              s,//.*?\n,//\n,g;
143
              s,(?<!include )"(\\.|[^"\\\n])*","",g;
144
              s,SPOT_API ,,g' -0777 <$file >$tmp
145

146
    $GREP '[	 ]$' $tmp &&
147
      diag 'Trailing whitespace.'
148

149
150
151
    case $file in
    *.test);;
    *)
Maximilien Colange's avatar
Maximilien Colange committed
152
      $GREP -E '(>[^>]*|^[^<]*)class[ \t]+[A-Z]' $tmp &&
153
154
        diag 'Use lower case class names.'

155
      $GREP '[	 ]if(' $tmp &&
156
	diag 'Missing space after "if"'
157

158
      $GREP '[	 ]if (.*).*{' $tmp &&
159
	diag 'Opening { should be on its own line.'
160

161
      $GREP '[	 ]if (.*).*;' $tmp &&
162
	diag 'if body should be on another line.'
163

164
      $GREP '[	 ]else.*;' $tmp &&
165
	diag 'else body should be on another line.'
166

167
      $GREP '[	 ]while(' $tmp &&
168
	diag 'Missing space after "while"'
169

170
      $GREP '[	 ]while (.*).*{' $tmp &&
171
	diag 'Opening { should be on its own line.'
172

173
      $GREP '[	 ]while (.*).*[^)];' $tmp &&
174
	diag 'while body should be on another line.'
175

176
      $GREP '[	 ]for(' $tmp &&
177
	diag 'Missing space after "for"'
178

179
      $GREP '[	 ]for (.*).*{' $tmp &&
180
	diag 'Opening { should be on its own line.'
181

182
      $GREP '[	 ]for (.*;.*;.*).*;' $tmp &&
183
	diag 'for body should be on another line.'
184

185
      $GREP '[	 ]switch(' $tmp &&
186
	diag 'Missing space after "switch"'
187

188
      $GREP '[	 ]switch (.*).*{' $tmp &&
189
	diag 'Opening { should be on its own line.'
190

191
      $GREP 'namespace .*{' $tmp &&
192
	diag 'Opening { should be on its own line.'
193

194
      $GREP 'class .*{' $tmp | $GREP -v enum &&
195
	diag 'Opening { should be on its own line.'
196

197
      $GREP '( ' $tmp &&
198
	diag 'No space after opening (.'
199

200
      $GREP ' )' $tmp &&
201
	diag 'No space before closing ).'
202

203
      $GREP '! ' $tmp &&
204
	diag 'No space after unary operators (!).'
205

206
      $GREP ",[^ \"	%'\\\\]" $tmp &&
207
	diag 'Space after coma.'
208

Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
209
      # The 'r' allows operator&&
210
      # The '.' allows &&...
211
      $GREP '[^	 r]&&[^	 .]' $tmp &&
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
212
	diag 'Space around binary operators.'
213

Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
214
      # The 'r' allows operator||
215
      $GREP '[^	 r]||[^	 ]' $tmp &&
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
216
	diag 'Space around binary operators.'
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
217
218

      # The 'r' allows operator==
219
      $GREP '[^	 r<>][!<>=]=[^	 ]' $tmp &&
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
220
221
222
	diag 'Space around binary operators.'

      # The 'r' allows operator<<=
223
      $GREP '[^	 r][<>][<>]=[^	 ]' $tmp &&
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
224
225
	diag 'Space around binary operators.'

226
      $GREP 'operator[^a-zA-Z0-9_(]*[	 ][^a-zA-Z0-9_(]*(' $tmp &&
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
227
228
        diag 'Write operatorXX(...) without spaces around XX.'

229
      $GREP 'operator[^(]* (' $tmp &&
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
230
        diag 'No space before ('
231

232
      $GREP '[	 ]default:[^:].*;' $tmp &&
233
	diag 'Label should be on their own line.'
234

235
      $GREP '[	 ]case.*:[^:].*;' $tmp &&
236
	diag 'Label should be on their own line.'
237

238
      $GREP '[	 ];' $tmp &&
239
	diag 'No space before semicolon.'
240

241
      $GREP -v 'for (.*;;)' $tmp | $GREP ';[^	 ")'"']" &&
242
	diag 'Must have space or newline after semicolon.'
243

244
245
246
247
248
      # Allow several { or } on the same line only if they are mixed
      # with parentheses, as this often occur with lambdas or
      # initializer lists.  What we want to forbid is cases where
      # multiple scopes are opened/closed on the same line.
      $GREP '^[^()]*}[^()]*}[^()]*$' $tmp &&
249
	diag 'No two } on the same line.'
250

251
      $GREP '^[^()]{[^()]*{[^()]$' $tmp &&
252
	diag 'No two { on the same line.'
253

Maximilien Colange's avatar
Maximilien Colange committed
254
255
      $GREP 'delete[	 ]*[(][^(]*[)];' $tmp |
      $GREP -v 'operator[	 ]*delete[	 ]*[(][^(]*[)];' &&
256
	diag 'No useless parentheses after delete.'
257

258
      $GREP 'return[	 ]*[(][^(]*[)];' $tmp &&
259
	diag 'No useless parentheses after return.'
260

261
      $GREP 'NULL' $tmp &&
262
	diag 'Use nullptr instead of NULL.'
263

264
265
      # std::list::size() can be O(n).  Better use empty() whenever
      # possible, even for other containers.
266
      e$GREP '(->|[.])size\(\) [=!]= 0|![a-zA-Z0-9_]*(->|[.])size\(\)|(if |while |assert)\([a-zA-Z0-9_]*(->|[.])size\(\)\)' $tmp &&
267
	diag 'Prefer empty() to check emptiness.'
268

Maximilien Colange's avatar
Maximilien Colange committed
269
      e$GREP 'assert\((0|!".*")\)' $tmp &&
270
271
        diag 'Prefer SPOT_UNREACHABLE or SPOT_UNIMPLEMENTED.'

272
      e$GREP '^[^=*<]*([+][+]|--);' $tmp &&
273
	diag 'Take good habits: use ++i instead of i++ when you have the choice.'
274

275
      $GREP '[^a-zA-Z0-9_](\*[a-zA-Z0-9_]*)\.' $tmp &&
276
	diag 'Use "x->y", not "(*x).y"'
277

278
      # we allow these functions only in ?...:...
279
      e$GREP 'bdd_(false|true)[	]*\(' $tmp | $GREP -v '[?:]' &&
280
281
	diag 'Use bddfalse and bddtrue instead of bdd_false() and bdd_true()'

282
283
284
285
286
287
288
      res=`perl -ne '$/ = undef;
	print "$&\n"
	while /if \((.*)(\s*==\s*0)?\)\s*delete(\[\])?\s+\1;(?!\s+else)/g' $tmp`
      if test -n "$res"; then
	 echo "$res"
	 diag 'No "if (x)" required before "delete x;".'
      fi
289

290
291
      case $file in
	*.hh | *.hxx)
292
	  if e$GREP '(<<|>>)' $tmp >/dev/null; then
293
294
	    :
	  else
295
	    $GREP '#.*include.*<iostream>' $tmp &&
296
297
	      diag 'Avoid <iostream> in headers, better use <iosfwd>.'
	  fi
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
298
299
          $GREP '^[ ]*	' $tmp &&
              diag 'Use spaces instead of tabs.'
300
301
	  # Headers from spot/priv/ are not installed, so may only be
	  # included from *.cc files or from other spot/priv/ headers
302
303
	  # (in the latter case they do not have to specify the priv/
	  # directory, so they will not match this regex).
304
305
306
307
	  case $file in
	    */priv/*|*/bin/*);;
	    *)
	      $GREP '#.*include.*priv/' $tmp &&
308
		diag 'Do not include private headers in public headers.'
Maximilien Colange's avatar
Maximilien Colange committed
309
        $GREP '^[^#]*[^#_]assert[	 ]*(.*)' $tmp &&
310
          diag 'Use SPOT_ASSERT() instead of assert() in public headers.'
311
	      ;;
312

313
	  esac
314
315
	  ;;
	*.cc)
316
	  if $GREP 'namespace$' $tmp >/dev/null; then
317
318
319
	    :
	  else
	    # We only check classes, but the rule should apply to functions too
320
	    $GREP '^[	 ]*class[	 ]' $tmp &&
321
322
	      diag 'Private definitions must be in anonymous namespace.'
	  fi
323
324
          e$GREP '	' $tmp &&
	      diag 'Use spaces instead of tabs.'
325
326
	  ;;
      esac
327
328
329
330
331
332
333
334
335
      case $file in
	*/bin/*|*.cc);;
	*)
	  $GREP -v '#.*include.*priv/' $tmp | $GREP '#.*include.*"' &&
	      diag 'Use <header> instead of "header" for public headers.'
	  $GREP '#.*include.*<spot/priv/.*>' $tmp &&
	      diag 'Use "spot/priv/..." instead of <spot/priv/...>".'
	  ;;
      esac
336
      ;;
337
338
    esac

339

340
    $fail && echo "$file" >>failures.style
341
342

  done || : # Make sure sh does not abort when read exits with false.
343
344
done

Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
345
346

# Rules for Makefiles.
347
for dir in "${INCDIR-..}" "${INCDIR-..}/../bin" "${INCDIR-..}/../tests"; do
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
348
349
350
351
352
353
354
355

  find "$dir" -name "Makefile.am" -a -type f -a -print |
  while read file; do
    fail=false

    # Strip comments.
    sed 's,#.*,,' < $file > $tmp

356
    $GREP '[	 ]$' $tmp &&
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
357
358
      diag 'Trailing whitespace.'

359
    $GREP '\.libs/' $tmp &&
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
360
361
	diag "Don't reference files in .libs/, use Libtool instead."

362
    $fail && echo "$file" >>failures.style
Alexandre Duret-Lutz's avatar
Alexandre Duret-Lutz committed
363
364
365
  done || : # Make sure sh does not abort when read exits with false.
done

366
if test -f failures.style; then
367
   echo "The following files contain style errors:"
368
369
   cat failures.style
   rm failures.style
370
371
   exit 1;
fi
372
373

exit 0