VirtualBox

source: vbox/trunk/src/bldprogs/VBoxCompilerPlugInsGcc.cpp@ 56987

Last change on this file since 56987 was 56977, checked in by vboxsync, 10 years ago

gccplugin: restructured the code a little, should be able to detect missing arguments now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.9 KB
Line 
1/* $Id: VBoxCompilerPlugInsGcc.cpp 56977 2015-07-18 17:43:19Z vboxsync $ */
2/** @file
3 * gccplugin - GCC plugin for checking IPRT format strings.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*********************************************************************************************************************************
19* Header Files *
20*********************************************************************************************************************************/
21#include <stdio.h>
22#include <iprt/cdefs.h>
23#include <iprt/stdarg.h>
24
25#include "plugin.h"
26#include "basic-block.h"
27#include "gimple.h"
28#include "tree.h"
29#include "tree-pass.h"
30#include "cp/cp-tree.h"
31
32#include "VBoxCompilerPlugIns.h"
33
34
35/*********************************************************************************************************************************
36* Global Variables *
37*********************************************************************************************************************************/
38/** License indicator. */
39int plugin_is_GPL_compatible;
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45/** Convencience macro not present in earlier gcc versions. */
46#ifndef VAR_P
47# define VAR_P(a_hNode) (TREE_CODE(a_hNode) == VAR_DECL)
48#endif
49
50
51/** For use with messages.
52 * @todo needs some more work... Actually, seems we're a bit handicapped by
53 * working on gimplified stuff. */
54#define MY_LOC(a_hPreferred, a_pState) EXPR_LOC_OR_LOC(a_hPreferred, (a_pState)->hFmtLoc)
55
56
57
58/*********************************************************************************************************************************
59* Internal Functions *
60*********************************************************************************************************************************/
61static bool MyPassGateCallback(void);
62static unsigned int MyPassExecuteCallback(void);
63static tree AttributeHandler(tree *, tree, tree, int, bool *);
64
65
66/*********************************************************************************************************************************
67* Global Variables *
68*********************************************************************************************************************************/
69/** Plug-in info. */
70static const struct plugin_info g_PlugInInfo =
71{
72 .version = "0.0.0-ALPHA",
73 .help = "Implements the __iprt_format__ attribute for checking format strings and arguments."
74};
75
76/** My pass. */
77static struct gimple_opt_pass g_MyPass =
78{
79 .pass =
80 {
81 .type = GIMPLE_PASS,
82 .name = "*iprt-format-checks", /* asterisk = no dump */
83 .optinfo_flags = 0,
84 .gate = MyPassGateCallback,
85 .execute = MyPassExecuteCallback,
86 .sub = NULL,
87 .next = NULL,
88 .static_pass_number = 0,
89 .tv_id = TV_NONE,
90 .properties_required = 0,
91 .properties_provided = 0,
92 .properties_destroyed = 0,
93 .todo_flags_start = 0,
94 .todo_flags_finish = 0,
95 }
96};
97
98/** The registration info for my pass. */
99static const struct register_pass_info g_MyPassInfo =
100{
101 .pass = &g_MyPass.pass,
102 .reference_pass_name = "ssa",
103 .ref_pass_instance_number = 1,
104 .pos_op = PASS_POS_INSERT_BEFORE,
105};
106
107
108/** Attribute specification. */
109static const struct attribute_spec g_AttribSpecs[] =
110{
111 {
112 .name = "iprt_format",
113 .min_length = 2,
114 .max_length = 2,
115 .decl_required = false,
116 .type_required = true,
117 .function_type_required = true,
118 .handler = AttributeHandler,
119 .affects_type_identity = false
120 },
121 { NULL, 0, 0, false, false, false, NULL, false }
122};
123
124
125#ifdef DEBUG
126
127/**
128 * Debug function for printing the scope of a decl.
129 * @param hDecl Declaration to print scope for.
130 */
131static void dprintScope(tree hDecl)
132{
133# if 0 /* later? */
134 tree hScope = CP_DECL_CONTEXT(hDecl);
135 if (hScope == global_namespace)
136 return;
137 if (TREE_CODE(hScope) == RECORD_TYPE)
138 hScope = TYPE_NAME(hScope);
139
140 /* recurse */
141 dprintScope(hScope);
142
143 /* name the scope. */
144 dprintf("::%s", DECL_NAME(hScope) ? IDENTIFIER_POINTER(DECL_NAME(hScope)) : "<noname>");
145# endif
146}
147
148
149/**
150 * Debug function for printing a declaration.
151 * @param hDecl The declaration to print.
152 */
153static void dprintDecl(tree hDecl)
154{
155 enum tree_code const enmDeclCode = TREE_CODE(hDecl);
156 tree const hType = TREE_TYPE(hDecl);
157 enum tree_code const enmTypeCode = hType ? TREE_CODE(hType) : (enum tree_code)-1;
158#if 0
159 if ( enmTypeCode == RECORD_TYPE
160 && enmDeclCode == TYPE_DECL
161 && DECL_ARTIFICIAL(hDecl))
162 dprint_class(hType);
163#endif
164
165 dprintf("%s ", tree_code_name[enmDeclCode]);
166 dprintScope(hDecl);
167 dprintf("::%s", DECL_NAME(hDecl) ? IDENTIFIER_POINTER(DECL_NAME(hDecl)) : "<noname>");
168 if (hType)
169 dprintf(" type %s", tree_code_name[enmTypeCode]);
170 dprintf(" @%s:%d", DECL_SOURCE_FILE(hDecl), DECL_SOURCE_LINE(hDecl));
171}
172
173#endif /* DEBUG */
174
175
176static location_t MyGetLocationPlusColumnOffset(location_t hLoc, unsigned int offColumn)
177{
178 /*
179 * Skip NOOPs, reserved locations and macro expansion.
180 */
181 if ( offColumn != 0
182 && hLoc >= RESERVED_LOCATION_COUNT
183 && !linemap_location_from_macro_expansion_p(line_table, hLoc))
184 {
185#if __GNUC__ >= 5 /** @todo figure this... */
186 /*
187 * There is an API for doing this, nice.
188 */
189 location_t hNewLoc = linemap_position_for_loc_and_offset(line_table, hLoc, offColumn);
190 if (hNewLoc && hNewLoc != hLoc)
191 {
192 dprintf("MyGetLocationPlusColumnOffset: hNewLoc=%#x hLoc=%#x offColumn=%u\n", hNewLoc, hLoc, offColumn);
193 return hNewLoc;
194 }
195
196#else
197 /*
198 * Have to do the job ourselves, it seems. This is a bit hairy...
199 */
200 line_map const *pMap = NULL;
201 location_t hLoc2 = linemap_resolve_location(line_table, hLoc, LRK_SPELLING_LOCATION, &pMap);
202 if (hLoc2)
203 hLoc = hLoc2;
204
205 /* Guard against wrap arounds and overlaps. */
206 if ( hLoc + offColumn > MAP_START_LOCATION(pMap) /** @todo Use MAX_SOURCE_LOCATION? */
207 && ( pMap == LINEMAPS_LAST_ORDINARY_MAP(line_table)
208 || hLoc + offColumn < MAP_START_LOCATION((pMap + 1))))
209 {
210 /* Calc new column and check that it's within the valid range. */
211 unsigned int uColumn = SOURCE_COLUMN(pMap, hLoc) + offColumn;
212 if (uColumn < RT_BIT_32(ORDINARY_MAP_NUMBER_OF_COLUMN_BITS(pMap)))
213 {
214 /* Try add the position. If we get a valid result, replace the location. */
215 source_location hNewLoc = linemap_position_for_line_and_column((line_map *)pMap, SOURCE_LINE(pMap, hLoc), uColumn);
216 if ( hNewLoc <= line_table->highest_location
217 && linemap_lookup(line_table, hNewLoc) != NULL)
218 {
219 dprintf("MyGetLocationPlusColumnOffset: hNewLoc=%#x hLoc=%#x offColumn=%u uColumn=%u\n",
220 hNewLoc, hLoc, offColumn, uColumn);
221 return hNewLoc;
222 }
223 }
224 }
225#endif
226 }
227 dprintf("MyGetLocationPlusColumnOffset: taking fallback\n");
228 return hLoc;
229}
230
231
232static location_t MyGetFormatStringLocation(PVFMTCHKSTATE pState, const char *pszLoc)
233{
234 location_t hLoc = pState->hFmtLoc;
235 intptr_t offString = pszLoc - pState->pszFmt;
236 if ( offString >= 0
237 && !linemap_location_from_macro_expansion_p(line_table, hLoc))
238 {
239 unsigned uCol = 1 + offString;
240 expanded_location XLoc = expand_location_to_spelling_point(hLoc);
241 int cchLine = 0;
242#if __GNUC__ >= 5 /** @todo figure this... */
243 const char *pszLine = location_get_source_line(XLoc, &cchLine);
244#else
245 const char *pszLine = location_get_source_line(XLoc);
246 if (pszLine)
247 {
248 const char *pszEol = strpbrk(pszLine, "\n\r");
249 if (!pszEol)
250 pszEol = strchr(pszLine, '\0');
251 cchLine = (int)(pszEol - pszLine);
252 }
253#endif
254 if (pszLine)
255 {
256 /** @todo Adjust the position by parsing the source. */
257 pszLine += XLoc.column - 1;
258 cchLine -= XLoc.column - 1;
259 }
260
261 hLoc = MyGetLocationPlusColumnOffset(hLoc, uCol);
262 }
263 return hLoc;
264}
265
266
267/**
268 * Non-recursive worker for MyCheckFormatRecursive.
269 *
270 * This will attempt to result @a hFmtArg into a string literal which it then
271 * passes on to MyCheckFormatString for the actual analyzis.
272 *
273 * @param pState The format string checking state.
274 * @param hFmtArg The format string node.
275 */
276DECL_NO_INLINE(static, void) MyCheckFormatNonRecursive(PVFMTCHKSTATE pState, tree hFmtArg)
277{
278 dprintf("checker: hFmtArg=%p %s\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)]);
279
280 /*
281 * Try resolve variables into constant strings.
282 */
283 if (VAR_P(hFmtArg))
284 {
285 hFmtArg = decl_constant_value(hFmtArg);
286 STRIP_NOPS(hFmtArg); /* Used as argument and assigned call result. */
287 dprintf("checker1: variable => hFmtArg=%p %s\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)]);
288 }
289
290 /*
291 * Fend off NULLs.
292 */
293 if (integer_zerop(hFmtArg))
294 warning_at(MY_LOC(hFmtArg, pState), 0, "Format string should not be NULL");
295 /*
296 * Need address expression to get any further.
297 */
298 else if (TREE_CODE(hFmtArg) != ADDR_EXPR)
299 dprintf("checker1: Not address expression (%s)\n", tree_code_name[TREE_CODE(hFmtArg)]);
300 else
301 {
302 pState->hFmtLoc = EXPR_LOC_OR_LOC(hFmtArg, pState->hFmtLoc);
303 hFmtArg = TREE_OPERAND(hFmtArg, 0);
304
305 /*
306 * Deal with fixed string indexing, if possible.
307 */
308 HOST_WIDE_INT off = 0;
309 if ( TREE_CODE(hFmtArg) == ARRAY_REF
310 && TREE_INT_CST(TREE_OPERAND(hFmtArg, 1)).fits_shwi() )
311 {
312 off = TREE_INT_CST(TREE_OPERAND(hFmtArg, 1)).to_shwi();
313 if (off < 0)
314 {
315 dprintf("checker1: ARRAY_REF, off=%ld\n", off);
316 return;
317 }
318 hFmtArg = TREE_OPERAND(hFmtArg, 0);
319 dprintf("checker1: ARRAY_REF => hFmtArg=%p %s, off=%ld\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)], off);
320 }
321
322 /*
323 * Deal with static const char g_szFmt[] = "qwerty"; Take care as
324 * the actual string constant may not necessarily include the terminator.
325 */
326 tree hArraySize = NULL_TREE;
327 if ( VAR_P(hFmtArg)
328 && TREE_CODE(TREE_TYPE(hFmtArg)) == ARRAY_TYPE)
329 {
330 tree hArrayInitializer = decl_constant_value(hFmtArg);
331 if ( hArrayInitializer != hFmtArg
332 && TREE_CODE(hArrayInitializer) == STRING_CST)
333 {
334 hArraySize = DECL_SIZE_UNIT(hFmtArg);
335 hFmtArg = hArrayInitializer;
336 }
337 }
338
339 /*
340 * Are we dealing with a string literal now?
341 */
342 if (TREE_CODE(hFmtArg) != STRING_CST)
343 dprintf("checker1: Not string literal (%s)\n", tree_code_name[TREE_CODE(hFmtArg)]);
344 else if (TYPE_MAIN_VARIANT(TREE_TYPE(TREE_TYPE(hFmtArg))) != char_type_node)
345 warning_at(pState->hFmtLoc, 0, "expected 'char' type string literal");
346 else
347 {
348 /*
349 * Yes we are, so get the pointer to the string and its length.
350 */
351 const char *pszFmt = TREE_STRING_POINTER(hFmtArg);
352 int cchFmt = TREE_STRING_LENGTH(hFmtArg);
353
354 /* Adjust cchFmt to the initialized array size if appropriate. */
355 if (hArraySize != NULL_TREE)
356 {
357 if (TREE_CODE(hArraySize) != INTEGER_CST)
358 warning_at(pState->hFmtLoc, 0, "Expected integer array size (not %s)", tree_code_name[TREE_CODE(hArraySize)]);
359 else if (!TREE_INT_CST(hArraySize).fits_shwi())
360 warning_at(pState->hFmtLoc, 0, "Unexpected integer overflow in array size constant");
361 else
362 {
363 HOST_WIDE_INT cbArray = TREE_INT_CST(hArraySize).to_shwi();
364 if ( cbArray <= 0
365 || cbArray != (int)cbArray)
366 warning_at(pState->hFmtLoc, 0, "Unexpected integer array size constant value: %ld", cbArray);
367 else if (cchFmt > cbArray)
368 {
369 dprintf("checker1: cchFmt=%d => cchFmt=%ld (=cbArray)\n", cchFmt, cbArray);
370 cchFmt = (int)cbArray;
371 }
372 }
373 }
374
375 /* Apply the offset, if given. */
376 if (off)
377 {
378 if (off >= cchFmt)
379 {
380 dprintf("checker1: off=%ld >= cchFmt=%d -> skipping\n", off, cchFmt);
381 return;
382 }
383 pszFmt += off;
384 cchFmt -= (int)off;
385 }
386
387 /*
388 * Check for unterminated strings.
389 */
390 if ( cchFmt < 1
391 || pszFmt[cchFmt - 1] != '\0')
392 warning_at(pState->hFmtLoc, 0, "Unterminated format string (cchFmt=%d)", cchFmt);
393 /*
394 * Call worker to check the actual string.
395 */
396 else
397 MyCheckFormatCString(pState, pszFmt);
398 }
399 }
400}
401
402
403/**
404 * Deal recursively with special format string constructs.
405 *
406 * This will call MyCheckFormatNonRecursive to validate each format string.
407 *
408 * @param pState The format string checking state.
409 * @param hFmtArg The format string node.
410 */
411static void MyCheckFormatRecursive(PVFMTCHKSTATE pState, tree hFmtArg)
412{
413 /*
414 * NULL format strings may cause crashes.
415 */
416 if (integer_zerop(hFmtArg))
417 warning_at(MY_LOC(hFmtArg, pState), 0, "Format string should not be NULL");
418 /*
419 * Check both branches of a ternary operator.
420 */
421 else if (TREE_CODE(hFmtArg) == COND_EXPR)
422 {
423 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 1));
424 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 2));
425 }
426 /*
427 * Strip coercion.
428 */
429 else if ( CONVERT_EXPR_P(hFmtArg)
430 && TYPE_PRECISION(TREE_TYPE(hFmtArg)) == TYPE_PRECISION(TREE_TYPE(TREE_OPERAND(hFmtArg, 0))) )
431 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 0));
432 /*
433 * We're good, hand it to the non-recursive worker.
434 */
435 else
436 MyCheckFormatNonRecursive(pState, hFmtArg);
437}
438
439
440/**
441 * Execute my pass.
442 * @returns Flags indicates stuff todo, we return 0.
443 */
444static unsigned int MyPassExecuteCallback(void)
445{
446 dprintf("MyPassExecuteCallback:\n");
447
448 /*
449 * Enumerate the basic blocks.
450 */
451 basic_block hBasicBlock;
452 FOR_EACH_BB(hBasicBlock)
453 {
454 dprintf(" hBasicBlock=%p\n", hBasicBlock);
455
456 /*
457 * Enumerate the statements in the current basic block.
458 * We're interested in calls to functions with the __iprt_format__ attribute.
459 */
460 for (gimple_stmt_iterator hStmtItr = gsi_start_bb(hBasicBlock); !gsi_end_p(hStmtItr); gsi_next(&hStmtItr))
461 {
462 gimple const hStmt = gsi_stmt(hStmtItr);
463 enum gimple_code const enmCode = gimple_code(hStmt);
464#ifdef DEBUG
465 unsigned const cOps = gimple_num_ops(hStmt);
466 dprintf(" hStmt=%p %s (%d) ops=%d\n", hStmt, gimple_code_name[enmCode], enmCode, cOps);
467 for (unsigned iOp = 0; iOp < cOps; iOp++)
468 {
469 tree const hOp = gimple_op(hStmt, iOp);
470 if (hOp)
471 dprintf(" %02d: %p, code %s(%d)\n", iOp, hOp, tree_code_name[TREE_CODE(hOp)], TREE_CODE(hOp));
472 else
473 dprintf(" %02d: NULL_TREE\n", iOp);
474 }
475#endif
476 if (enmCode == GIMPLE_CALL)
477 {
478 /*
479 * Check if the function type has the __iprt_format__ attribute.
480 */
481 tree const hFn = gimple_call_fn(hStmt);
482 tree const hFnType = gimple_call_fntype(hStmt);
483 tree const hAttr = lookup_attribute("iprt_format", TYPE_ATTRIBUTES(hFnType));
484#ifdef DEBUG
485 tree const hFnDecl = gimple_call_fndecl(hStmt);
486 dprintf(" hFn =%p %s(%d); args=%d\n",
487 hFn, tree_code_name[TREE_CODE(hFn)], TREE_CODE(hFn), gimple_call_num_args(hStmt));
488 if (hFnDecl)
489 dprintf(" hFnDecl=%p %s(%d) type=%p %s:%d\n", hFnDecl, tree_code_name[TREE_CODE(hFnDecl)],
490 TREE_CODE(hFnDecl), TREE_TYPE(hFnDecl), DECL_SOURCE_FILE(hFnDecl), DECL_SOURCE_LINE(hFnDecl));
491 if (hFnType)
492 dprintf(" hFnType=%p %s(%d)\n", hFnType, tree_code_name[TREE_CODE(hFnType)], TREE_CODE(hFnType));
493#endif
494 if (hAttr)
495 {
496 /*
497 * Yeah, it has the attribute!
498 */
499 tree const hAttrArgs = TREE_VALUE(hAttr);
500 VFMTCHKSTATE State;
501 State.iFmt = TREE_INT_CST(TREE_VALUE(hAttrArgs)).to_shwi();
502 State.iArgs = TREE_INT_CST(TREE_VALUE(TREE_CHAIN(hAttrArgs))).to_shwi();
503 State.hStmt = hStmt;
504 State.hFmtLoc = gimple_location(hStmt);
505 State.pszFmt = NULL;
506 dprintf(" %s() __iprt_format__(iFmt=%ld, iArgs=%ld)\n",
507 DECL_NAME(hFnDecl) ? IDENTIFIER_POINTER(DECL_NAME(hFnDecl)) : "<unamed>", State.iFmt, State.iArgs);
508
509 MyCheckFormatRecursive(&State, gimple_call_arg(hStmt, State.iFmt - 1));
510 }
511 }
512 }
513 }
514 return 0;
515}
516
517
518/**
519 * Gate callback for my pass that indicates whether it should execute or not.
520 * @returns true to execute.
521 */
522static bool MyPassGateCallback(void)
523{
524 dprintf("MyPassGateCallback:\n");
525 return true;
526}
527
528
529/**
530 * Validate the use of an attribute.
531 *
532 * @returns ??
533 * @param phOnNode The node the attribute is being used on.
534 * @param hAttrName The attribute name.
535 * @param hAttrArgs The attribute arguments.
536 * @param fFlags Some kind of flags...
537 * @param pfDontAddAttrib Whether to add the attribute to this node or not.
538 */
539static tree AttributeHandler(tree *phOnNode, tree hAttrName, tree hAttrArgs, int fFlags, bool *pfDontAddAttrib)
540{
541 dprintf("AttributeHandler: name=%s fFlags=%#x", IDENTIFIER_POINTER(hAttrName), fFlags);
542 long iFmt = TREE_INT_CST(TREE_VALUE(hAttrArgs)).to_shwi();
543 long iArgs = TREE_INT_CST(TREE_VALUE(TREE_CHAIN(hAttrArgs))).to_shwi();
544 dprintf(" iFmt=%ld iArgs=%ld", iFmt, iArgs);
545
546 tree hType = *phOnNode;
547 dprintf(" hType=%p %s(%d)\n", hType, tree_code_name[TREE_CODE(hType)], TREE_CODE(hType));
548
549 if (pfDontAddAttrib)
550 *pfDontAddAttrib = false;
551 return NULL_TREE;
552}
553
554
555/**
556 * Called when we can register attributes.
557 *
558 * @param pvEventData Ignored.
559 * @param pvUser Ignored.
560 */
561static void RegisterAttributesEvent(void *pvEventData, void *pvUser)
562{
563 NOREF(pvEventData); NOREF(pvUser);
564 dprintf("RegisterAttributesEvent: pvEventData=%p\n", pvEventData);
565
566 register_attribute(&g_AttribSpecs[0]);
567}
568
569
570/**
571 * The plug-in entry point.
572 *
573 * @returns 0 to indicate success?
574 * @param pPlugInInfo Plugin info structure.
575 * @param pGccVer GCC Version.
576 */
577int plugin_init(plugin_name_args *pPlugInInfo, plugin_gcc_version *pGccVer)
578{
579 dprintf("plugin_init: %s\n", pPlugInInfo->full_name);
580 dprintf("gcc version: basever=%s datestamp=%s devphase=%s revision=%s\n",
581 pGccVer->basever, pGccVer->datestamp, pGccVer->devphase, pGccVer->revision);
582
583 /* Ask for callback in which we may register the attribute. */
584 register_callback(pPlugInInfo->base_name, PLUGIN_ATTRIBUTES, RegisterAttributesEvent, NULL /*pvUser*/);
585
586 /* Register our pass. */
587 register_callback(pPlugInInfo->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, (void *)&g_MyPassInfo);
588
589 /* Register plug-in info. */
590 register_callback(pPlugInInfo->base_name, PLUGIN_INFO, NULL, (void *)&g_PlugInInfo);
591
592 return 0;
593}
594
595
596
597
598/*
599 *
600 * Functions used by the common code.
601 * Functions used by the common code.
602 * Functions used by the common code.
603 *
604 */
605
606void VFmtChkWarnFmt(PVFMTCHKSTATE pState, const char *pszLoc, const char *pszFormat, ...)
607{
608 char szTmp[1024];
609 va_list va;
610 va_start(va, pszFormat);
611 vsnprintf(szTmp, sizeof(szTmp), pszFormat, va);
612 va_end(va);
613
614 /* display the warning. */
615 warning_at(MyGetFormatStringLocation(pState, pszLoc), 0, "%s", szTmp);
616}
617
618
619
620void VFmtChkVerifyEndOfArgs(PVFMTCHKSTATE pState, unsigned iArg)
621{
622 dprintf("VFmtChkVerifyFinalArg: iArg=%u iArgs=%ld cArgs=%u\n", iArg, pState->iArgs, gimple_call_num_args(pState->hStmt));
623 if (pState->iArgs > 0)
624 {
625 iArg += pState->iArgs - 1;
626 unsigned cArgs = gimple_call_num_args(pState->hStmt);
627 if (iArg == cArgs)
628 { /* fine */ }
629 else if (iArg < cArgs)
630 {
631 tree hArg = gimple_call_arg(pState->hStmt, iArg);
632 if (cArgs - iArg > 1)
633 warning_at(MY_LOC(hArg, pState), 0, "%u extra arguments not consumed by format string", cArgs - iArg);
634 else if ( TREE_CODE(hArg) != INTEGER_CST
635 || !TREE_INT_CST(hArg).fits_shwi()
636 || TREE_INT_CST(hArg).to_shwi() != -99) /* ignore final dummy argument: ..., -99); */
637 warning_at(MY_LOC(hArg, pState), 0, "one extra argument not consumed by format string");
638 }
639 /* This should be handled elsewhere, but just in case. */
640 else if (iArg - 1 == cArgs)
641 warning_at(pState->hFmtLoc, 0, "one argument too few");
642 else
643 warning_at(pState->hFmtLoc, 0, "%u arguments too few", iArg - cArgs);
644 }
645}
646
647
648bool VFmtChkRequirePresentArg(PVFMTCHKSTATE pState, const char *pszLoc, unsigned iArg, const char *pszMessage)
649{
650 if (pState->iArgs > 0)
651 {
652 iArg += pState->iArgs - 1;
653 unsigned cArgs = gimple_call_num_args(pState->hStmt);
654 if (iArg >= cArgs)
655 {
656 VFmtChkWarnFmt(pState, pszLoc, "Missing argument! %s", pszMessage);
657 return false;
658 }
659 }
660 return true;
661}
662
663
664bool VFmtChkRequireIntArg(PVFMTCHKSTATE pState, const char *pszLoc, unsigned iArg, const char *pszMessage)
665{
666 if (VFmtChkRequirePresentArg(pState, pszLoc, iArg, pszMessage))
667 {
668 /** @todo type check. */
669 return true;
670 }
671 return false;
672}
673
674
675bool VFmtChkRequireStringArg(PVFMTCHKSTATE pState, const char *pszLoc, unsigned iArg, const char *pszMessage)
676{
677 if (VFmtChkRequirePresentArg(pState, pszLoc, iArg, pszMessage))
678 {
679 /** @todo type check. */
680 return true;
681 }
682 return false;
683}
684
685
686bool VFmtChkRequireVaListPtrArg(PVFMTCHKSTATE pState, const char *pszLoc, unsigned iArg, const char *pszMessage)
687{
688 if (VFmtChkRequirePresentArg(pState, pszLoc, iArg, pszMessage))
689 {
690 /** @todo type check. */
691 return true;
692 }
693 return false;
694}
695
696
697
698void VFmtChkHandleReplacementFormatString(PVFMTCHKSTATE pState, const char *pszPctM, unsigned iArg)
699{
700 if (pState->iArgs > 0)
701 {
702 pState->iArgs += iArg;
703
704 }
705}
706
707
708const char *VFmtChkGetFmtLocFile(PVFMTCHKSTATE pState)
709{
710 return LOCATION_FILE(pState->hFmtLoc);
711}
712
713
714unsigned int VFmtChkGetFmtLocLine(PVFMTCHKSTATE pState)
715{
716 return LOCATION_LINE(pState->hFmtLoc);
717}
718
719
720unsigned int VFmtChkGetFmtLocColumn(PVFMTCHKSTATE pState)
721{
722 return LOCATION_COLUMN(pState->hFmtLoc);
723}
724
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette