Rapid spam filtering system https://rspamd.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

822 lines
20 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*-
  2. * Copyright 2016 Vsevolod Stakhov
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <sys/types.h>
  17. #include "printf_check.h"
  18. #include "clang/AST/AST.h"
  19. #include "clang/AST/Expr.h"
  20. #include "clang/AST/ASTConsumer.h"
  21. #include "clang/AST/RecursiveASTVisitor.h"
  22. #include <unordered_map>
  23. #include <unordered_set>
  24. #include <vector>
  25. #include <sstream>
  26. #include <ctype.h>
  27. #include <signal.h>
  28. #include <assert.h>
  29. #include <cstdint>
  30. using namespace clang;
  31. namespace rspamd {
  32. struct PrintfArgChecker;
  33. static bool cstring_arg_handler (const Expr *arg,
  34. struct PrintfArgChecker *ctx);
  35. static bool int_arg_handler (const Expr *arg,
  36. struct PrintfArgChecker *ctx);
  37. static bool long_arg_handler (const Expr *arg,
  38. struct PrintfArgChecker *ctx);
  39. static bool size_arg_handler (const Expr *arg,
  40. struct PrintfArgChecker *ctx);
  41. static bool char_arg_handler (const Expr *arg,
  42. struct PrintfArgChecker *ctx);
  43. static bool double_arg_handler (const Expr *arg,
  44. struct PrintfArgChecker *ctx);
  45. static bool long_double_arg_handler (const Expr *arg,
  46. struct PrintfArgChecker *ctx);
  47. static bool pointer_arg_handler (const Expr *arg,
  48. struct PrintfArgChecker *ctx);
  49. static bool pid_arg_handler (const Expr *arg,
  50. struct PrintfArgChecker *ctx);
  51. static bool time_arg_handler (const Expr *arg,
  52. struct PrintfArgChecker *ctx);
  53. static bool int64_arg_handler (const Expr *arg,
  54. struct PrintfArgChecker *ctx);
  55. static bool int32_arg_handler (const Expr *arg,
  56. struct PrintfArgChecker *ctx);
  57. static bool tok_arg_handler (const Expr *arg,
  58. struct PrintfArgChecker *ctx);
  59. static bool fstring_arg_handler (const Expr *arg,
  60. struct PrintfArgChecker *ctx);
  61. static bool gstring_arg_handler (const Expr *arg,
  62. struct PrintfArgChecker *ctx);
  63. static bool gerr_arg_handler (const Expr *arg,
  64. struct PrintfArgChecker *ctx);
  65. using arg_parser_t = bool (*) (const Expr *, struct PrintfArgChecker *);
  66. static void
  67. print_error (const char *err, const Expr *e, const ASTContext *ast,
  68. CompilerInstance *ci)
  69. {
  70. auto loc = e->getExprLoc ();
  71. auto &diag = ci->getDiagnostics ();
  72. auto id = diag.getCustomDiagID (DiagnosticsEngine::Error,
  73. "format query error: %0");
  74. diag.Report (loc, id) << err;
  75. }
  76. static void
  77. print_warning (const char *err, const Expr *e, const ASTContext *ast,
  78. CompilerInstance *ci)
  79. {
  80. auto loc = e->getExprLoc ();
  81. auto &diag = ci->getDiagnostics ();
  82. auto id = diag.getCustomDiagID (DiagnosticsEngine::Warning,
  83. "format query warning: %0");
  84. diag.Report (loc, id) << err;
  85. }
  86. static void
  87. print_remark (const char *err, const Expr *e, const ASTContext *ast,
  88. CompilerInstance *ci)
  89. {
  90. auto loc = e->getExprLoc ();
  91. auto &diag = ci->getDiagnostics ();
  92. auto id = diag.getCustomDiagID (DiagnosticsEngine::Remark,
  93. "format query warning: %0");
  94. diag.Report (loc, id) << err;
  95. }
  96. struct PrintfArgChecker {
  97. private:
  98. arg_parser_t parser;
  99. public:
  100. int width;
  101. int precision;
  102. bool is_unsigned;
  103. ASTContext *past;
  104. CompilerInstance *pci;
  105. PrintfArgChecker (arg_parser_t _p, ASTContext *_ast, CompilerInstance *_ci) :
  106. parser (_p), past (_ast), pci(_ci)
  107. {
  108. width = 0;
  109. precision = 0;
  110. is_unsigned = false;
  111. }
  112. virtual ~PrintfArgChecker ()
  113. {
  114. }
  115. bool operator() (const Expr *e)
  116. {
  117. return parser (e, this);
  118. }
  119. };
  120. class PrintfCheckVisitor::impl {
  121. std::unordered_map<std::string, unsigned int> printf_functions;
  122. std::unordered_set<char> format_specs;
  123. ASTContext *pcontext;
  124. CompilerInstance *ci;
  125. std::unique_ptr <PrintfArgChecker> parseFlags (const std::string &flags,
  126. const Expr *e)
  127. {
  128. auto type = flags.back ();
  129. switch (type) {
  130. case 's':
  131. return std::make_unique<PrintfArgChecker> (cstring_arg_handler,
  132. this->pcontext, this->ci);
  133. case 'd':
  134. return std::make_unique<PrintfArgChecker> (int_arg_handler,
  135. this->pcontext, this->ci);
  136. case 'z':
  137. return std::make_unique<PrintfArgChecker> (size_arg_handler,
  138. this->pcontext, this->ci);
  139. case 'l':
  140. return std::make_unique<PrintfArgChecker> (long_arg_handler,
  141. this->pcontext, this->ci);
  142. case 'f':
  143. case 'g':
  144. return std::make_unique<PrintfArgChecker> (double_arg_handler,
  145. this->pcontext, this->ci);
  146. case 'F':
  147. case 'G':
  148. return std::make_unique<PrintfArgChecker> (
  149. long_double_arg_handler,
  150. this->pcontext, this->ci);
  151. case 'c':
  152. return std::make_unique<PrintfArgChecker> (char_arg_handler,
  153. this->pcontext, this->ci);
  154. case 'p':
  155. return std::make_unique<PrintfArgChecker> (pointer_arg_handler,
  156. this->pcontext, this->ci);
  157. case 'P':
  158. return std::make_unique<PrintfArgChecker> (pid_arg_handler,
  159. this->pcontext, this->ci);
  160. case 't':
  161. return std::make_unique<PrintfArgChecker> (time_arg_handler,
  162. this->pcontext, this->ci);
  163. case 'L':
  164. return std::make_unique<PrintfArgChecker> (int64_arg_handler,
  165. this->pcontext, this->ci);
  166. case 'D':
  167. return std::make_unique<PrintfArgChecker> (int32_arg_handler,
  168. this->pcontext, this->ci);
  169. case 'T':
  170. return std::make_unique<PrintfArgChecker> (tok_arg_handler,
  171. this->pcontext, this->ci);
  172. case 'V':
  173. return std::make_unique<PrintfArgChecker> (fstring_arg_handler,
  174. this->pcontext, this->ci);
  175. case 'v':
  176. return std::make_unique<PrintfArgChecker> (gstring_arg_handler,
  177. this->pcontext, this->ci);
  178. case 'e':
  179. return std::make_unique<PrintfArgChecker> (gerr_arg_handler,
  180. this->pcontext, this->ci);
  181. default: {
  182. auto err_msg = std::string ("unknown parser flag: ") + type;
  183. print_warning (err_msg.c_str(),
  184. e, this->pcontext, this->ci);
  185. break;
  186. }
  187. }
  188. return nullptr;
  189. }
  190. std::shared_ptr <std::vector<PrintfArgChecker>>
  191. genParsers (const StringRef query, const Expr *e)
  192. {
  193. enum {
  194. ignore_chars = 0,
  195. read_percent,
  196. read_width,
  197. read_precision,
  198. read_arg
  199. } state = ignore_chars;
  200. int width, precision;
  201. std::string flags;
  202. auto res = std::make_shared<std::vector<PrintfArgChecker> > ();
  203. for (auto citer = query.begin(); citer != query.end(); ++citer) {
  204. auto c = *citer;
  205. switch (state) {
  206. case ignore_chars:
  207. if (c == '%') {
  208. state = read_percent;
  209. flags.clear ();
  210. width = precision = 0;
  211. }
  212. break;
  213. case read_percent:
  214. if (isdigit (c)) {
  215. state = read_width;
  216. width = c - '0';
  217. }
  218. else if (c == '.') {
  219. state = read_precision;
  220. precision = c - '0';
  221. }
  222. else if (c == '*') {
  223. /* %*s - need integer argument */
  224. res->emplace_back (int_arg_handler, this->pcontext,
  225. this->ci);
  226. if (*std::next (citer) == '.') {
  227. ++citer;
  228. state = read_precision;
  229. }
  230. else {
  231. state = read_arg;
  232. }
  233. }
  234. else if (c == '%') {
  235. /* Percent character, ignore */
  236. state = ignore_chars;
  237. }
  238. else {
  239. // Rewind iter
  240. --citer;
  241. state = read_arg;
  242. }
  243. break;
  244. case read_width:
  245. if (isdigit (c)) {
  246. width *= 10;
  247. width += c - '0';
  248. }
  249. else if (c == '.') {
  250. state = read_precision;
  251. precision = c - '0';
  252. }
  253. else {
  254. // Rewind iter
  255. --citer;
  256. state = read_arg;
  257. }
  258. break;
  259. case read_precision:
  260. if (isdigit (c)) {
  261. precision *= 10;
  262. precision += c - '0';
  263. }
  264. else if (c == '*') {
  265. res->emplace_back (int_arg_handler, this->pcontext,
  266. this->ci);
  267. state = read_arg;
  268. }
  269. else {
  270. // Rewind iter
  271. --citer;
  272. state = read_arg;
  273. }
  274. break;
  275. case read_arg:
  276. auto found = format_specs.find (c);
  277. if (found != format_specs.end () || !isalpha (c)) {
  278. if (isalpha (c)) {
  279. flags.push_back (c);
  280. }
  281. auto handler = parseFlags (flags, e);
  282. if (handler) {
  283. auto handler_copy = *handler;
  284. handler_copy.precision = precision;
  285. handler_copy.width = width;
  286. res->emplace_back (std::move (handler_copy));
  287. }
  288. else {
  289. return nullptr;
  290. }
  291. if (c == '%') {
  292. state = read_percent;
  293. }
  294. else {
  295. state = ignore_chars;
  296. }
  297. flags.clear ();
  298. width = precision = 0;
  299. }
  300. else {
  301. flags.push_back (c);
  302. }
  303. break;
  304. }
  305. }
  306. if (state == read_arg) {
  307. auto handler = parseFlags (flags, e);
  308. if (handler) {
  309. auto handler_copy = *handler;
  310. handler_copy.precision = precision;
  311. handler_copy.width = width;
  312. res->emplace_back (std::move (handler_copy));
  313. }
  314. else {
  315. return nullptr;
  316. }
  317. }
  318. return res;
  319. }
  320. public:
  321. impl (ASTContext *_ctx, clang::CompilerInstance &_ci)
  322. : pcontext (_ctx), ci(&_ci)
  323. {
  324. /* name -> format string position */
  325. printf_functions = {
  326. {"rspamd_printf", 0},
  327. {"rspamd_default_log_function", 4},
  328. {"rspamd_snprintf", 2},
  329. {"rspamd_fprintf", 1},
  330. {"rspamd_printf_gstring", 1},
  331. {"rspamd_printf_fstring", 1},
  332. {"rspamd_conditional_debug_fast", 6},
  333. };
  334. format_specs = {
  335. 's', 'd', 'l', 'L', 'v', 'V', 'f', 'F', 'g', 'G',
  336. 'T', 'z', 'D', 'c', 'p', 'P', 'e'
  337. };
  338. };
  339. bool VisitCallExpr (CallExpr *E)
  340. {
  341. if (E->getCalleeDecl () == nullptr) {
  342. print_remark ("cannot get callee decl",
  343. E, this->pcontext, this->ci);
  344. return true;
  345. }
  346. auto callee = dyn_cast<NamedDecl> (E->getCalleeDecl ());
  347. if (callee == NULL) {
  348. print_remark ("cannot get named callee decl",
  349. E, this->pcontext, this->ci);
  350. return true;
  351. }
  352. auto fname = callee->getNameAsString ();
  353. auto pos_it = printf_functions.find (fname);
  354. if (pos_it != printf_functions.end ()) {
  355. const auto args = E->getArgs ();
  356. auto pos = pos_it->second;
  357. auto query = args[pos];
  358. if (!query->isEvaluatable (*pcontext)) {
  359. print_remark ("cannot evaluate query",
  360. E, this->pcontext, this->ci);
  361. /* It is not assumed to be an error */
  362. return true;
  363. }
  364. clang::Expr::EvalResult r;
  365. if (!query->EvaluateAsRValue (r, *pcontext)) {
  366. print_warning ("cannot evaluate rvalue of query",
  367. E, this->pcontext, this->ci);
  368. return false;
  369. }
  370. auto qval = dyn_cast<StringLiteral> (
  371. r.Val.getLValueBase ().get<const Expr *> ());
  372. if (!qval) {
  373. print_warning ("bad or absent query string",
  374. E, this->pcontext, this->ci);
  375. return false;
  376. }
  377. auto parsers = genParsers (qval->getString (), E);
  378. if (parsers) {
  379. if (parsers->size () != E->getNumArgs () - (pos + 1)) {
  380. std::ostringstream err_buf;
  381. err_buf << "number of arguments for " << fname
  382. << " mismatches query string '" <<
  383. qval->getString ().str ()
  384. << "', expected " << parsers->size () <<
  385. " args"
  386. << ", got " <<
  387. (E->getNumArgs () - (pos + 1))
  388. << " args";
  389. print_error (err_buf.str().c_str(), E, this->pcontext, this->ci);
  390. return false;
  391. }
  392. else {
  393. for (auto i = pos + 1; i < E->getNumArgs (); i++) {
  394. auto arg = args[i];
  395. if (arg) {
  396. if (!parsers->at (i - (pos + 1)) (arg)) {
  397. return false;
  398. }
  399. }
  400. }
  401. }
  402. }
  403. }
  404. return true;
  405. }
  406. };
  407. PrintfCheckVisitor::PrintfCheckVisitor (ASTContext *ctx,
  408. clang::CompilerInstance &ci) :
  409. pimpl{new impl (ctx, ci)}
  410. {
  411. }
  412. PrintfCheckVisitor::~PrintfCheckVisitor ()
  413. {
  414. }
  415. bool PrintfCheckVisitor::VisitCallExpr (clang::CallExpr *E)
  416. {
  417. return pimpl->VisitCallExpr (E);
  418. }
  419. /* Type handlers */
  420. static bool
  421. cstring_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  422. {
  423. auto type = arg->getType ().split ().Ty;
  424. if (!type->isPointerType ()) {
  425. auto err_msg = std::string ("bad string argument for %s: ") +
  426. arg->getType ().getAsString ();
  427. print_error (err_msg.c_str(),
  428. arg, ctx->past, ctx->pci);
  429. return false;
  430. }
  431. auto ptr_type = type->getPointeeType ().split ().Ty;
  432. if (!ptr_type->isCharType ()) {
  433. /* We might have gchar * here */
  434. auto desugared_type = ptr_type->getUnqualifiedDesugaredType ();
  435. auto desugared_ptr_type = type->getUnqualifiedDesugaredType ();
  436. if (!desugared_type || (!desugared_type->isCharType () &&
  437. !desugared_ptr_type->isVoidPointerType ())) {
  438. if (desugared_type) {
  439. desugared_type->dump ();
  440. }
  441. auto err_msg = std::string ("bad string argument for %s: ") +
  442. arg->getType ().getAsString ();
  443. print_error (err_msg.c_str(),
  444. arg, ctx->past, ctx->pci);
  445. return false;
  446. }
  447. }
  448. return true;
  449. }
  450. static bool
  451. check_builtin_type (const Expr *arg, struct PrintfArgChecker *ctx,
  452. const std::vector <BuiltinType::Kind> &k, const std::string &fmt)
  453. {
  454. auto type = arg->getType ().split ().Ty;
  455. auto desugared_type = type->getUnqualifiedDesugaredType ();
  456. if (!desugared_type->isBuiltinType ()) {
  457. auto err_msg = std::string ("not a builtin type for ") + fmt + " arg: " +
  458. arg->getType ().getAsString ();
  459. print_error (err_msg.c_str(),
  460. arg, ctx->past, ctx->pci);
  461. return false;
  462. }
  463. auto builtin_type = dyn_cast<BuiltinType> (desugared_type);
  464. auto kind = builtin_type->getKind ();
  465. auto found = false;
  466. for (auto kk : k) {
  467. if (kind == kk) {
  468. found = true;
  469. break;
  470. }
  471. }
  472. if (!found) {
  473. auto err_msg = std::string ("bad argument for ") +
  474. fmt + " arg: " +
  475. arg->getType ().getAsString () +
  476. ", resolved as: " +
  477. builtin_type->getNameAsCString (ctx->past->getPrintingPolicy ());
  478. print_error (err_msg.c_str(),
  479. arg, ctx->past, ctx->pci);
  480. return false;
  481. }
  482. return true;
  483. }
  484. static bool
  485. int_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  486. {
  487. return check_builtin_type (arg,
  488. ctx,
  489. {BuiltinType::Kind::UInt,
  490. BuiltinType::Kind::Int},
  491. "%d or *");
  492. }
  493. static bool
  494. long_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  495. {
  496. return check_builtin_type (arg,
  497. ctx,
  498. {BuiltinType::Kind::ULong,
  499. BuiltinType::Kind::Long},
  500. "%l");
  501. }
  502. static bool
  503. char_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  504. {
  505. return check_builtin_type (arg,
  506. ctx,
  507. {BuiltinType::Kind::UChar,
  508. BuiltinType::Kind::SChar,
  509. BuiltinType::Kind::Int}, // Because of char -> int propagation
  510. "%c");
  511. }
  512. static bool
  513. size_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  514. {
  515. if (sizeof (size_t) == sizeof (long)) {
  516. if (sizeof (long long) == sizeof (long)) {
  517. return check_builtin_type (arg,
  518. ctx,
  519. {BuiltinType::Kind::ULong,
  520. BuiltinType::Kind::Long,
  521. BuiltinType::Kind::LongLong,
  522. BuiltinType::Kind::ULongLong},
  523. "%z");
  524. }
  525. else {
  526. return check_builtin_type (arg,
  527. ctx,
  528. {BuiltinType::Kind::ULong,
  529. BuiltinType::Kind::Long},
  530. "%z");
  531. }
  532. }
  533. else if (sizeof (size_t) == sizeof (int)) {
  534. return check_builtin_type (arg,
  535. ctx,
  536. {BuiltinType::Kind::UInt,
  537. BuiltinType::Kind::Int},
  538. "%z");
  539. }
  540. else {
  541. assert (0);
  542. }
  543. return true;
  544. }
  545. static bool
  546. double_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  547. {
  548. return check_builtin_type (arg,
  549. ctx,
  550. {BuiltinType::Kind::Double},
  551. "%f or %g");
  552. }
  553. static bool
  554. long_double_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  555. {
  556. return check_builtin_type (arg,
  557. ctx,
  558. {BuiltinType::Kind::LongDouble},
  559. "%F or %G");
  560. }
  561. static bool
  562. pid_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  563. {
  564. if (sizeof (pid_t) == sizeof (long)) {
  565. return check_builtin_type (arg,
  566. ctx,
  567. {BuiltinType::Kind::ULong,
  568. BuiltinType::Kind::Long},
  569. "%P");
  570. }
  571. else if (sizeof (pid_t) == sizeof (int)) {
  572. return check_builtin_type (arg,
  573. ctx,
  574. {BuiltinType::Kind::UInt,
  575. BuiltinType::Kind::Int},
  576. "%P");
  577. }
  578. else {
  579. assert (0);
  580. }
  581. }
  582. static bool
  583. time_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  584. {
  585. if (sizeof (time_t) == sizeof (long)) {
  586. return check_builtin_type (arg,
  587. ctx,
  588. {BuiltinType::Kind::ULong,
  589. BuiltinType::Kind::Long},
  590. "%t");
  591. }
  592. else if (sizeof (time_t) == sizeof (int)) {
  593. return check_builtin_type (arg,
  594. ctx,
  595. {BuiltinType::Kind::UInt,
  596. BuiltinType::Kind::Int},
  597. "%t");
  598. }
  599. else {
  600. assert (0);
  601. }
  602. }
  603. static bool
  604. pointer_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  605. {
  606. auto type = arg->getType ().split ().Ty;
  607. if (!type->isPointerType ()) {
  608. auto err_msg = std::string ("bad pointer argument for %p: ") +
  609. arg->getType ().getAsString ();
  610. print_error (err_msg.c_str(),
  611. arg, ctx->past, ctx->pci);
  612. return false;
  613. }
  614. return true;
  615. }
  616. static bool
  617. int64_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  618. {
  619. std::vector <BuiltinType::Kind> check;
  620. if (sizeof (int64_t) == sizeof (long long)) {
  621. check.push_back (BuiltinType::Kind::ULongLong);
  622. check.push_back (BuiltinType::Kind::LongLong);
  623. }
  624. if (sizeof (int64_t) == sizeof (long)) {
  625. check.push_back (BuiltinType::Kind::ULong);
  626. check.push_back (BuiltinType::Kind::Long);
  627. }
  628. return check_builtin_type (arg,
  629. ctx,
  630. check,
  631. "%L");
  632. return true;
  633. }
  634. static bool
  635. int32_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  636. {
  637. std::vector < BuiltinType::Kind> check;
  638. if (sizeof (int32_t) == sizeof (long)) {
  639. check.push_back (BuiltinType::Kind::ULong);
  640. check.push_back (BuiltinType::Kind::Long);
  641. }
  642. if (sizeof (int32_t) == sizeof (int)) {
  643. check.push_back (BuiltinType::Kind::UInt);
  644. check.push_back (BuiltinType::Kind::Int);
  645. }
  646. return check_builtin_type (arg,
  647. ctx,
  648. check,
  649. "%D");
  650. return true;
  651. }
  652. static bool
  653. check_struct_type (const Expr *arg, struct PrintfArgChecker *ctx,
  654. const std::string &sname, const std::string &fmt)
  655. {
  656. auto type = arg->getType ().split ().Ty;
  657. if (!type->isPointerType ()) {
  658. auto err_msg = std::string ("non pointer argument for %s: ") +
  659. arg->getType ().getAsString ();
  660. print_error (err_msg.c_str(),
  661. arg, ctx->past, ctx->pci);
  662. return false;
  663. }
  664. auto ptr_type = type->getPointeeType ().split ().Ty;
  665. auto desugared_type = ptr_type->getUnqualifiedDesugaredType ();
  666. if (!desugared_type->isRecordType ()) {
  667. auto err_msg = std::string ("not a record type for ") + fmt + " arg: " +
  668. arg->getType ().getAsString ();
  669. print_error (err_msg.c_str(),
  670. arg, ctx->past, ctx->pci);
  671. return false;
  672. }
  673. auto struct_type = desugared_type->getAsStructureType ();
  674. auto struct_decl = struct_type->getDecl ();
  675. auto struct_def = struct_decl->getNameAsString ();
  676. if (struct_def != sname) {
  677. auto err_msg = std::string ("bad argument '") + struct_def + "' for "
  678. + fmt + " arg: " +
  679. arg->getType ().getAsString ();
  680. print_error (err_msg.c_str(),
  681. arg, ctx->past, ctx->pci);
  682. return false;
  683. }
  684. return true;
  685. }
  686. static bool
  687. tok_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  688. {
  689. return check_struct_type (arg,
  690. ctx,
  691. "f_str_tok",
  692. "%T");
  693. }
  694. static bool
  695. fstring_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  696. {
  697. return check_struct_type (arg,
  698. ctx,
  699. "f_str_s",
  700. "%V");
  701. }
  702. static bool
  703. gstring_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  704. {
  705. return check_struct_type (arg,
  706. ctx,
  707. "_GString",
  708. "%v");
  709. }
  710. static bool
  711. gerr_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx)
  712. {
  713. return check_struct_type (arg,
  714. ctx,
  715. "_GError",
  716. "%e");
  717. }
  718. }