1*4c3eb207Smrg /* A state machine for detecting misuses of <stdio.h>'s FILE * API.
2*4c3eb207Smrg Copyright (C) 2019-2020 Free Software Foundation, Inc.
3*4c3eb207Smrg Contributed by David Malcolm <dmalcolm@redhat.com>.
4*4c3eb207Smrg
5*4c3eb207Smrg This file is part of GCC.
6*4c3eb207Smrg
7*4c3eb207Smrg GCC is free software; you can redistribute it and/or modify it
8*4c3eb207Smrg under the terms of the GNU General Public License as published by
9*4c3eb207Smrg the Free Software Foundation; either version 3, or (at your option)
10*4c3eb207Smrg any later version.
11*4c3eb207Smrg
12*4c3eb207Smrg GCC is distributed in the hope that it will be useful, but
13*4c3eb207Smrg WITHOUT ANY WARRANTY; without even the implied warranty of
14*4c3eb207Smrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15*4c3eb207Smrg General Public License for more details.
16*4c3eb207Smrg
17*4c3eb207Smrg You should have received a copy of the GNU General Public License
18*4c3eb207Smrg along with GCC; see the file COPYING3. If not see
19*4c3eb207Smrg <http://www.gnu.org/licenses/>. */
20*4c3eb207Smrg
21*4c3eb207Smrg #include "config.h"
22*4c3eb207Smrg #include "system.h"
23*4c3eb207Smrg #include "coretypes.h"
24*4c3eb207Smrg #include "tree.h"
25*4c3eb207Smrg #include "function.h"
26*4c3eb207Smrg #include "basic-block.h"
27*4c3eb207Smrg #include "gimple.h"
28*4c3eb207Smrg #include "options.h"
29*4c3eb207Smrg #include "diagnostic-path.h"
30*4c3eb207Smrg #include "diagnostic-metadata.h"
31*4c3eb207Smrg #include "function.h"
32*4c3eb207Smrg #include "analyzer/analyzer.h"
33*4c3eb207Smrg #include "diagnostic-event-id.h"
34*4c3eb207Smrg #include "analyzer/analyzer-logging.h"
35*4c3eb207Smrg #include "analyzer/sm.h"
36*4c3eb207Smrg #include "analyzer/pending-diagnostic.h"
37*4c3eb207Smrg #include "analyzer/function-set.h"
38*4c3eb207Smrg #include "analyzer/analyzer-selftests.h"
39*4c3eb207Smrg
40*4c3eb207Smrg #if ENABLE_ANALYZER
41*4c3eb207Smrg
42*4c3eb207Smrg namespace ana {
43*4c3eb207Smrg
44*4c3eb207Smrg namespace {
45*4c3eb207Smrg
46*4c3eb207Smrg /* A state machine for detecting misuses of <stdio.h>'s FILE * API. */
47*4c3eb207Smrg
48*4c3eb207Smrg class fileptr_state_machine : public state_machine
49*4c3eb207Smrg {
50*4c3eb207Smrg public:
51*4c3eb207Smrg fileptr_state_machine (logger *logger);
52*4c3eb207Smrg
inherited_state_p() const53*4c3eb207Smrg bool inherited_state_p () const FINAL OVERRIDE { return false; }
54*4c3eb207Smrg
55*4c3eb207Smrg bool on_stmt (sm_context *sm_ctxt,
56*4c3eb207Smrg const supernode *node,
57*4c3eb207Smrg const gimple *stmt) const FINAL OVERRIDE;
58*4c3eb207Smrg
59*4c3eb207Smrg void on_condition (sm_context *sm_ctxt,
60*4c3eb207Smrg const supernode *node,
61*4c3eb207Smrg const gimple *stmt,
62*4c3eb207Smrg tree lhs,
63*4c3eb207Smrg enum tree_code op,
64*4c3eb207Smrg tree rhs) const FINAL OVERRIDE;
65*4c3eb207Smrg
66*4c3eb207Smrg bool can_purge_p (state_t s) const FINAL OVERRIDE;
67*4c3eb207Smrg pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
68*4c3eb207Smrg
69*4c3eb207Smrg /* Start state. */
70*4c3eb207Smrg state_t m_start;
71*4c3eb207Smrg
72*4c3eb207Smrg /* State for a FILE * returned from fopen that hasn't been checked for
73*4c3eb207Smrg NULL.
74*4c3eb207Smrg It could be an open stream, or could be NULL. */
75*4c3eb207Smrg state_t m_unchecked;
76*4c3eb207Smrg
77*4c3eb207Smrg /* State for a FILE * that's known to be NULL. */
78*4c3eb207Smrg state_t m_null;
79*4c3eb207Smrg
80*4c3eb207Smrg /* State for a FILE * that's known to be a non-NULL open stream. */
81*4c3eb207Smrg state_t m_nonnull;
82*4c3eb207Smrg
83*4c3eb207Smrg /* State for a FILE * that's had fclose called on it. */
84*4c3eb207Smrg state_t m_closed;
85*4c3eb207Smrg
86*4c3eb207Smrg /* Stop state, for a FILE * we don't want to track any more. */
87*4c3eb207Smrg state_t m_stop;
88*4c3eb207Smrg };
89*4c3eb207Smrg
90*4c3eb207Smrg /* Base class for diagnostics relative to fileptr_state_machine. */
91*4c3eb207Smrg
92*4c3eb207Smrg class file_diagnostic : public pending_diagnostic
93*4c3eb207Smrg {
94*4c3eb207Smrg public:
file_diagnostic(const fileptr_state_machine & sm,tree arg)95*4c3eb207Smrg file_diagnostic (const fileptr_state_machine &sm, tree arg)
96*4c3eb207Smrg : m_sm (sm), m_arg (arg)
97*4c3eb207Smrg {}
98*4c3eb207Smrg
subclass_equal_p(const pending_diagnostic & base_other) const99*4c3eb207Smrg bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
100*4c3eb207Smrg {
101*4c3eb207Smrg return same_tree_p (m_arg, ((const file_diagnostic &)base_other).m_arg);
102*4c3eb207Smrg }
103*4c3eb207Smrg
describe_state_change(const evdesc::state_change & change)104*4c3eb207Smrg label_text describe_state_change (const evdesc::state_change &change)
105*4c3eb207Smrg OVERRIDE
106*4c3eb207Smrg {
107*4c3eb207Smrg if (change.m_old_state == m_sm.m_start
108*4c3eb207Smrg && change.m_new_state == m_sm.m_unchecked)
109*4c3eb207Smrg // TODO: verify that it's the fopen stmt, not a copy
110*4c3eb207Smrg return label_text::borrow ("opened here");
111*4c3eb207Smrg if (change.m_old_state == m_sm.m_unchecked
112*4c3eb207Smrg && change.m_new_state == m_sm.m_nonnull)
113*4c3eb207Smrg return change.formatted_print ("assuming %qE is non-NULL",
114*4c3eb207Smrg change.m_expr);
115*4c3eb207Smrg if (change.m_new_state == m_sm.m_null)
116*4c3eb207Smrg return change.formatted_print ("assuming %qE is NULL",
117*4c3eb207Smrg change.m_expr);
118*4c3eb207Smrg return label_text ();
119*4c3eb207Smrg }
120*4c3eb207Smrg
121*4c3eb207Smrg protected:
122*4c3eb207Smrg const fileptr_state_machine &m_sm;
123*4c3eb207Smrg tree m_arg;
124*4c3eb207Smrg };
125*4c3eb207Smrg
126*4c3eb207Smrg class double_fclose : public file_diagnostic
127*4c3eb207Smrg {
128*4c3eb207Smrg public:
double_fclose(const fileptr_state_machine & sm,tree arg)129*4c3eb207Smrg double_fclose (const fileptr_state_machine &sm, tree arg)
130*4c3eb207Smrg : file_diagnostic (sm, arg)
131*4c3eb207Smrg {}
132*4c3eb207Smrg
get_kind() const133*4c3eb207Smrg const char *get_kind () const FINAL OVERRIDE { return "double_fclose"; }
134*4c3eb207Smrg
emit(rich_location * rich_loc)135*4c3eb207Smrg bool emit (rich_location *rich_loc) FINAL OVERRIDE
136*4c3eb207Smrg {
137*4c3eb207Smrg return warning_at (rich_loc, OPT_Wanalyzer_double_fclose,
138*4c3eb207Smrg "double %<fclose%> of FILE %qE",
139*4c3eb207Smrg m_arg);
140*4c3eb207Smrg }
141*4c3eb207Smrg
describe_state_change(const evdesc::state_change & change)142*4c3eb207Smrg label_text describe_state_change (const evdesc::state_change &change)
143*4c3eb207Smrg OVERRIDE
144*4c3eb207Smrg {
145*4c3eb207Smrg if (change.m_new_state == m_sm.m_closed)
146*4c3eb207Smrg {
147*4c3eb207Smrg m_first_fclose_event = change.m_event_id;
148*4c3eb207Smrg return change.formatted_print ("first %qs here", "fclose");
149*4c3eb207Smrg }
150*4c3eb207Smrg return file_diagnostic::describe_state_change (change);
151*4c3eb207Smrg }
152*4c3eb207Smrg
describe_final_event(const evdesc::final_event & ev)153*4c3eb207Smrg label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
154*4c3eb207Smrg {
155*4c3eb207Smrg if (m_first_fclose_event.known_p ())
156*4c3eb207Smrg return ev.formatted_print ("second %qs here; first %qs was at %@",
157*4c3eb207Smrg "fclose", "fclose",
158*4c3eb207Smrg &m_first_fclose_event);
159*4c3eb207Smrg return ev.formatted_print ("second %qs here", "fclose");
160*4c3eb207Smrg }
161*4c3eb207Smrg
162*4c3eb207Smrg private:
163*4c3eb207Smrg diagnostic_event_id_t m_first_fclose_event;
164*4c3eb207Smrg };
165*4c3eb207Smrg
166*4c3eb207Smrg class file_leak : public file_diagnostic
167*4c3eb207Smrg {
168*4c3eb207Smrg public:
file_leak(const fileptr_state_machine & sm,tree arg)169*4c3eb207Smrg file_leak (const fileptr_state_machine &sm, tree arg)
170*4c3eb207Smrg : file_diagnostic (sm, arg)
171*4c3eb207Smrg {}
172*4c3eb207Smrg
get_kind() const173*4c3eb207Smrg const char *get_kind () const FINAL OVERRIDE { return "file_leak"; }
174*4c3eb207Smrg
emit(rich_location * rich_loc)175*4c3eb207Smrg bool emit (rich_location *rich_loc) FINAL OVERRIDE
176*4c3eb207Smrg {
177*4c3eb207Smrg diagnostic_metadata m;
178*4c3eb207Smrg /* CWE-775: "Missing Release of File Descriptor or Handle after
179*4c3eb207Smrg Effective Lifetime". */
180*4c3eb207Smrg m.add_cwe (775);
181*4c3eb207Smrg return warning_meta (rich_loc, m, OPT_Wanalyzer_file_leak,
182*4c3eb207Smrg "leak of FILE %qE",
183*4c3eb207Smrg m_arg);
184*4c3eb207Smrg }
185*4c3eb207Smrg
describe_state_change(const evdesc::state_change & change)186*4c3eb207Smrg label_text describe_state_change (const evdesc::state_change &change)
187*4c3eb207Smrg FINAL OVERRIDE
188*4c3eb207Smrg {
189*4c3eb207Smrg if (change.m_new_state == m_sm.m_unchecked)
190*4c3eb207Smrg {
191*4c3eb207Smrg m_fopen_event = change.m_event_id;
192*4c3eb207Smrg return label_text::borrow ("opened here");
193*4c3eb207Smrg }
194*4c3eb207Smrg return file_diagnostic::describe_state_change (change);
195*4c3eb207Smrg }
196*4c3eb207Smrg
describe_final_event(const evdesc::final_event & ev)197*4c3eb207Smrg label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
198*4c3eb207Smrg {
199*4c3eb207Smrg if (m_fopen_event.known_p ())
200*4c3eb207Smrg return ev.formatted_print ("%qE leaks here; was opened at %@",
201*4c3eb207Smrg ev.m_expr, &m_fopen_event);
202*4c3eb207Smrg else
203*4c3eb207Smrg return ev.formatted_print ("%qE leaks here", ev.m_expr);
204*4c3eb207Smrg }
205*4c3eb207Smrg
206*4c3eb207Smrg private:
207*4c3eb207Smrg diagnostic_event_id_t m_fopen_event;
208*4c3eb207Smrg };
209*4c3eb207Smrg
210*4c3eb207Smrg /* fileptr_state_machine's ctor. */
211*4c3eb207Smrg
fileptr_state_machine(logger * logger)212*4c3eb207Smrg fileptr_state_machine::fileptr_state_machine (logger *logger)
213*4c3eb207Smrg : state_machine ("file", logger)
214*4c3eb207Smrg {
215*4c3eb207Smrg m_start = add_state ("start");
216*4c3eb207Smrg m_unchecked = add_state ("unchecked");
217*4c3eb207Smrg m_null = add_state ("null");
218*4c3eb207Smrg m_nonnull = add_state ("nonnull");
219*4c3eb207Smrg m_closed = add_state ("closed");
220*4c3eb207Smrg m_stop = add_state ("stop");
221*4c3eb207Smrg }
222*4c3eb207Smrg
223*4c3eb207Smrg /* Get a set of functions that are known to take a FILE * that must be open,
224*4c3eb207Smrg and are known to not close it. */
225*4c3eb207Smrg
226*4c3eb207Smrg static function_set
get_file_using_fns()227*4c3eb207Smrg get_file_using_fns ()
228*4c3eb207Smrg {
229*4c3eb207Smrg // TODO: populate this list more fully
230*4c3eb207Smrg static const char * const funcnames[] = {
231*4c3eb207Smrg /* This array must be kept sorted. */
232*4c3eb207Smrg "__fbufsize",
233*4c3eb207Smrg "__flbf",
234*4c3eb207Smrg "__fpending",
235*4c3eb207Smrg "__fpurge"
236*4c3eb207Smrg "__freadable",
237*4c3eb207Smrg "__freading",
238*4c3eb207Smrg "__fsetlocking",
239*4c3eb207Smrg "__fwritable",
240*4c3eb207Smrg "__fwriting",
241*4c3eb207Smrg "clearerr",
242*4c3eb207Smrg "clearerr_unlocked",
243*4c3eb207Smrg "feof",
244*4c3eb207Smrg "feof_unlocked",
245*4c3eb207Smrg "ferror",
246*4c3eb207Smrg "ferror_unlocked",
247*4c3eb207Smrg "fflush", // safe to call with NULL
248*4c3eb207Smrg "fflush_unlocked", // safe to call with NULL
249*4c3eb207Smrg "fgetc",
250*4c3eb207Smrg "fgetc_unlocked",
251*4c3eb207Smrg "fgetpos",
252*4c3eb207Smrg "fgets",
253*4c3eb207Smrg "fgets_unlocked",
254*4c3eb207Smrg "fgetwc_unlocked",
255*4c3eb207Smrg "fgetws_unlocked",
256*4c3eb207Smrg "fileno",
257*4c3eb207Smrg "fileno_unlocked",
258*4c3eb207Smrg "fprintf",
259*4c3eb207Smrg "fputc",
260*4c3eb207Smrg "fputc_unlocked",
261*4c3eb207Smrg "fputs",
262*4c3eb207Smrg "fputs_unlocked",
263*4c3eb207Smrg "fputwc_unlocked",
264*4c3eb207Smrg "fputws_unlocked",
265*4c3eb207Smrg "fread_unlocked",
266*4c3eb207Smrg "fseek",
267*4c3eb207Smrg "fsetpos",
268*4c3eb207Smrg "ftell",
269*4c3eb207Smrg "fwrite_unlocked",
270*4c3eb207Smrg "getc",
271*4c3eb207Smrg "getc_unlocked",
272*4c3eb207Smrg "getwc_unlocked",
273*4c3eb207Smrg "putc",
274*4c3eb207Smrg "putc_unlocked",
275*4c3eb207Smrg "rewind",
276*4c3eb207Smrg "setbuf",
277*4c3eb207Smrg "setbuffer",
278*4c3eb207Smrg "setlinebuf",
279*4c3eb207Smrg "setvbuf",
280*4c3eb207Smrg "ungetc",
281*4c3eb207Smrg "vfprintf"
282*4c3eb207Smrg };
283*4c3eb207Smrg const size_t count
284*4c3eb207Smrg = sizeof(funcnames) / sizeof (funcnames[0]);
285*4c3eb207Smrg function_set fs (funcnames, count);
286*4c3eb207Smrg return fs;
287*4c3eb207Smrg }
288*4c3eb207Smrg
289*4c3eb207Smrg /* Return true if FNDECL is known to require an open FILE *, and is known
290*4c3eb207Smrg to not close it. */
291*4c3eb207Smrg
292*4c3eb207Smrg static bool
is_file_using_fn_p(tree fndecl)293*4c3eb207Smrg is_file_using_fn_p (tree fndecl)
294*4c3eb207Smrg {
295*4c3eb207Smrg function_set fs = get_file_using_fns ();
296*4c3eb207Smrg return fs.contains_decl_p (fndecl);
297*4c3eb207Smrg }
298*4c3eb207Smrg
299*4c3eb207Smrg /* Implementation of state_machine::on_stmt vfunc for fileptr_state_machine. */
300*4c3eb207Smrg
301*4c3eb207Smrg bool
on_stmt(sm_context * sm_ctxt,const supernode * node,const gimple * stmt) const302*4c3eb207Smrg fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
303*4c3eb207Smrg const supernode *node,
304*4c3eb207Smrg const gimple *stmt) const
305*4c3eb207Smrg {
306*4c3eb207Smrg if (const gcall *call = dyn_cast <const gcall *> (stmt))
307*4c3eb207Smrg if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
308*4c3eb207Smrg {
309*4c3eb207Smrg if (is_named_call_p (callee_fndecl, "fopen", call, 2))
310*4c3eb207Smrg {
311*4c3eb207Smrg tree lhs = gimple_call_lhs (call);
312*4c3eb207Smrg if (lhs)
313*4c3eb207Smrg {
314*4c3eb207Smrg lhs = sm_ctxt->get_readable_tree (lhs);
315*4c3eb207Smrg sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked);
316*4c3eb207Smrg }
317*4c3eb207Smrg else
318*4c3eb207Smrg {
319*4c3eb207Smrg /* TODO: report leak. */
320*4c3eb207Smrg }
321*4c3eb207Smrg return true;
322*4c3eb207Smrg }
323*4c3eb207Smrg
324*4c3eb207Smrg if (is_named_call_p (callee_fndecl, "fclose", call, 1))
325*4c3eb207Smrg {
326*4c3eb207Smrg tree arg = gimple_call_arg (call, 0);
327*4c3eb207Smrg arg = sm_ctxt->get_readable_tree (arg);
328*4c3eb207Smrg
329*4c3eb207Smrg sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
330*4c3eb207Smrg
331*4c3eb207Smrg // TODO: is it safe to call fclose (NULL) ?
332*4c3eb207Smrg sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_closed);
333*4c3eb207Smrg sm_ctxt->on_transition (node, stmt, arg, m_null, m_closed);
334*4c3eb207Smrg
335*4c3eb207Smrg sm_ctxt->on_transition (node, stmt , arg, m_nonnull, m_closed);
336*4c3eb207Smrg
337*4c3eb207Smrg sm_ctxt->warn_for_state (node, stmt, arg, m_closed,
338*4c3eb207Smrg new double_fclose (*this, arg));
339*4c3eb207Smrg sm_ctxt->on_transition (node, stmt, arg, m_closed, m_stop);
340*4c3eb207Smrg return true;
341*4c3eb207Smrg }
342*4c3eb207Smrg
343*4c3eb207Smrg if (is_file_using_fn_p (callee_fndecl))
344*4c3eb207Smrg {
345*4c3eb207Smrg // TODO: operations on unchecked file
346*4c3eb207Smrg return true;
347*4c3eb207Smrg }
348*4c3eb207Smrg // etc
349*4c3eb207Smrg }
350*4c3eb207Smrg
351*4c3eb207Smrg return false;
352*4c3eb207Smrg }
353*4c3eb207Smrg
354*4c3eb207Smrg /* Implementation of state_machine::on_condition vfunc for
355*4c3eb207Smrg fileptr_state_machine.
356*4c3eb207Smrg Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
357*4c3eb207Smrg
358*4c3eb207Smrg void
on_condition(sm_context * sm_ctxt,const supernode * node,const gimple * stmt,tree lhs,enum tree_code op,tree rhs) const359*4c3eb207Smrg fileptr_state_machine::on_condition (sm_context *sm_ctxt,
360*4c3eb207Smrg const supernode *node,
361*4c3eb207Smrg const gimple *stmt,
362*4c3eb207Smrg tree lhs,
363*4c3eb207Smrg enum tree_code op,
364*4c3eb207Smrg tree rhs) const
365*4c3eb207Smrg {
366*4c3eb207Smrg if (!zerop (rhs))
367*4c3eb207Smrg return;
368*4c3eb207Smrg
369*4c3eb207Smrg // TODO: has to be a FILE *, specifically
370*4c3eb207Smrg if (TREE_CODE (TREE_TYPE (lhs)) != POINTER_TYPE)
371*4c3eb207Smrg return;
372*4c3eb207Smrg
373*4c3eb207Smrg // TODO: has to be a FILE *, specifically
374*4c3eb207Smrg if (TREE_CODE (TREE_TYPE (rhs)) != POINTER_TYPE)
375*4c3eb207Smrg return;
376*4c3eb207Smrg
377*4c3eb207Smrg if (op == NE_EXPR)
378*4c3eb207Smrg {
379*4c3eb207Smrg log ("got 'ARG != 0' match");
380*4c3eb207Smrg sm_ctxt->on_transition (node, stmt,
381*4c3eb207Smrg lhs, m_unchecked, m_nonnull);
382*4c3eb207Smrg }
383*4c3eb207Smrg else if (op == EQ_EXPR)
384*4c3eb207Smrg {
385*4c3eb207Smrg log ("got 'ARG == 0' match");
386*4c3eb207Smrg sm_ctxt->on_transition (node, stmt,
387*4c3eb207Smrg lhs, m_unchecked, m_null);
388*4c3eb207Smrg }
389*4c3eb207Smrg }
390*4c3eb207Smrg
391*4c3eb207Smrg /* Implementation of state_machine::can_purge_p vfunc for fileptr_state_machine.
392*4c3eb207Smrg Don't allow purging of pointers in state 'unchecked' or 'nonnull'
393*4c3eb207Smrg (to avoid false leak reports). */
394*4c3eb207Smrg
395*4c3eb207Smrg bool
can_purge_p(state_t s) const396*4c3eb207Smrg fileptr_state_machine::can_purge_p (state_t s) const
397*4c3eb207Smrg {
398*4c3eb207Smrg return s != m_unchecked && s != m_nonnull;
399*4c3eb207Smrg }
400*4c3eb207Smrg
401*4c3eb207Smrg /* Implementation of state_machine::on_leak vfunc for
402*4c3eb207Smrg fileptr_state_machine, for complaining about leaks of FILE * in
403*4c3eb207Smrg state 'unchecked' and 'nonnull'. */
404*4c3eb207Smrg
405*4c3eb207Smrg pending_diagnostic *
on_leak(tree var) const406*4c3eb207Smrg fileptr_state_machine::on_leak (tree var) const
407*4c3eb207Smrg {
408*4c3eb207Smrg return new file_leak (*this, var);
409*4c3eb207Smrg }
410*4c3eb207Smrg
411*4c3eb207Smrg } // anonymous namespace
412*4c3eb207Smrg
413*4c3eb207Smrg /* Internal interface to this file. */
414*4c3eb207Smrg
415*4c3eb207Smrg state_machine *
make_fileptr_state_machine(logger * logger)416*4c3eb207Smrg make_fileptr_state_machine (logger *logger)
417*4c3eb207Smrg {
418*4c3eb207Smrg return new fileptr_state_machine (logger);
419*4c3eb207Smrg }
420*4c3eb207Smrg
421*4c3eb207Smrg #if CHECKING_P
422*4c3eb207Smrg
423*4c3eb207Smrg namespace selftest {
424*4c3eb207Smrg
425*4c3eb207Smrg /* Run all of the selftests within this file. */
426*4c3eb207Smrg
427*4c3eb207Smrg void
analyzer_sm_file_cc_tests()428*4c3eb207Smrg analyzer_sm_file_cc_tests ()
429*4c3eb207Smrg {
430*4c3eb207Smrg function_set fs = get_file_using_fns ();
431*4c3eb207Smrg fs.assert_sorted ();
432*4c3eb207Smrg fs.assert_sane ();
433*4c3eb207Smrg }
434*4c3eb207Smrg
435*4c3eb207Smrg } // namespace selftest
436*4c3eb207Smrg
437*4c3eb207Smrg #endif /* CHECKING_P */
438*4c3eb207Smrg
439*4c3eb207Smrg } // namespace ana
440*4c3eb207Smrg
441*4c3eb207Smrg #endif /* #if ENABLE_ANALYZER */
442