xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/analyzer/sm-file.cc (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
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