Coverage Report

Created: 2023-09-25 06:15

/src/cppcheck/lib/preprocessor.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Cppcheck - A tool for static C/C++ code analysis
3
 * Copyright (C) 2007-2023 Cppcheck team.
4
 *
5
 * This program is free software: you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation, either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
20
#include "preprocessor.h"
21
22
#include "errorlogger.h"
23
#include "errortypes.h"
24
#include "library.h"
25
#include "path.h"
26
#include "platform.h"
27
#include "settings.h"
28
#include "standards.h"
29
#include "suppressions.h"
30
#include "utils.h"
31
32
#include <algorithm>
33
#include <array>
34
#include <cstddef>
35
#include <iterator> // back_inserter
36
#include <sstream> // IWYU pragma: keep
37
#include <utility>
38
39
#include <simplecpp.h>
40
41
static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2)
42
0
{
43
0
    return tok1 && tok2 && tok1->location.sameline(tok2->location);
44
0
}
45
46
/**
47
 * Remove heading and trailing whitespaces from the input parameter.
48
 * If string is all spaces/tabs, return empty string.
49
 * @param s The string to trim.
50
 */
51
static std::string trim(const std::string& s)
52
0
{
53
0
    const std::string::size_type beg = s.find_first_not_of(" \t");
54
0
    if (beg == std::string::npos)
55
0
        return "";
56
0
    const std::string::size_type end = s.find_last_not_of(" \t");
57
0
    return s.substr(beg, end - beg + 1);
58
0
}
59
60
Directive::Directive(std::string _file, const int _linenr, const std::string &_str) :
61
    file(std::move(_file)),
62
    linenr(_linenr),
63
    str(trim(_str))
64
0
{}
65
66
char Preprocessor::macroChar = char(1);
67
68
Preprocessor::Preprocessor(const Settings& settings, ErrorLogger *errorLogger) : mSettings(settings), mErrorLogger(errorLogger)
69
1.36k
{}
70
71
Preprocessor::~Preprocessor()
72
1.36k
{
73
1.36k
    for (const std::pair<const std::string, simplecpp::TokenList*>& tokenList : mTokenLists)
74
0
        delete tokenList.second;
75
1.36k
}
76
77
namespace {
78
    struct BadInlineSuppression {
79
0
        BadInlineSuppression(const simplecpp::Location &l, std::string msg) : location(l), errmsg(std::move(msg)) {}
80
        simplecpp::Location location;
81
        std::string errmsg;
82
    };
83
}
84
85
static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std::list<Suppressions::Suppression> &inlineSuppressions, std::list<BadInlineSuppression> &bad)
86
0
{
87
0
    const std::string cppchecksuppress("cppcheck-suppress");
88
89
0
    const std::string &comment = tok->str();
90
0
    if (comment.size() < cppchecksuppress.size())
91
0
        return false;
92
0
    const std::string::size_type pos1 = comment.find_first_not_of("/* \t");
93
0
    if (pos1 == std::string::npos)
94
0
        return false;
95
0
    if (pos1 + cppchecksuppress.size() >= comment.size())
96
0
        return false;
97
0
    if (comment.substr(pos1, cppchecksuppress.size()) != cppchecksuppress)
98
0
        return false;
99
100
    // skip spaces after "cppcheck-suppress"
101
0
    const std::string::size_type pos2 = comment.find_first_not_of(' ', pos1+cppchecksuppress.size());
102
0
    if (pos2 == std::string::npos)
103
0
        return false;
104
105
0
    if (comment[pos2] == '[') {
106
        // multi suppress format
107
0
        std::string errmsg;
108
0
        std::vector<Suppressions::Suppression> suppressions = Suppressions::parseMultiSuppressComment(comment, &errmsg);
109
110
0
        if (!errmsg.empty())
111
0
            bad.emplace_back(tok->location, std::move(errmsg));
112
113
0
        std::copy_if(suppressions.cbegin(), suppressions.cend(), std::back_inserter(inlineSuppressions), [](const Suppressions::Suppression& s) {
114
0
            return !s.errorId.empty();
115
0
        });
116
0
    } else {
117
        //single suppress format
118
0
        std::string errmsg;
119
0
        Suppressions::Suppression s;
120
0
        if (!s.parseComment(comment, &errmsg))
121
0
            return false;
122
123
0
        if (!s.errorId.empty())
124
0
            inlineSuppressions.push_back(std::move(s));
125
126
0
        if (!errmsg.empty())
127
0
            bad.emplace_back(tok->location, std::move(errmsg));
128
0
    }
129
130
0
    return true;
131
0
}
132
133
static void addinlineSuppressions(const simplecpp::TokenList &tokens, const Settings &settings, Suppressions &suppressions, std::list<BadInlineSuppression> &bad)
134
0
{
135
0
    for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
136
0
        if (!tok->comment)
137
0
            continue;
138
139
0
        std::list<Suppressions::Suppression> inlineSuppressions;
140
0
        if (!parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad))
141
0
            continue;
142
143
0
        if (!sameline(tok->previous, tok)) {
144
            // find code after comment..
145
0
            tok = tok->next;
146
0
            while (tok && tok->comment) {
147
0
                parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad);
148
0
                tok = tok->next;
149
0
            }
150
0
            if (!tok)
151
0
                break;
152
0
        }
153
154
0
        if (inlineSuppressions.empty())
155
0
            continue;
156
157
        // Relative filename
158
0
        std::string relativeFilename(tok->location.file());
159
0
        if (settings.relativePaths) {
160
0
            for (const std::string & basePath : settings.basePaths) {
161
0
                const std::string bp = basePath + "/";
162
0
                if (relativeFilename.compare(0,bp.size(),bp)==0) {
163
0
                    relativeFilename = relativeFilename.substr(bp.size());
164
0
                }
165
0
            }
166
0
        }
167
0
        relativeFilename = Path::simplifyPath(relativeFilename);
168
169
        // special handling when suppressing { warnings for backwards compatibility
170
0
        const bool thisAndNextLine = tok->previous &&
171
0
                                     tok->previous->previous &&
172
0
                                     tok->next &&
173
0
                                     !sameline(tok->previous->previous, tok->previous) &&
174
0
                                     tok->location.line + 1 == tok->next->location.line &&
175
0
                                     tok->location.fileIndex == tok->next->location.fileIndex &&
176
0
                                     tok->previous->str() == "{";
177
178
        // Add the suppressions.
179
0
        for (Suppressions::Suppression &suppr : inlineSuppressions) {
180
0
            suppr.fileName = relativeFilename;
181
0
            suppr.lineNumber = tok->location.line;
182
0
            suppr.thisAndNextLine = thisAndNextLine;
183
0
            suppressions.addSuppression(std::move(suppr));
184
0
        }
185
0
    }
186
0
}
187
188
void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, Suppressions &suppressions)
189
1.36k
{
190
1.36k
    if (!mSettings.inlineSuppressions)
191
1.36k
        return;
192
0
    std::list<BadInlineSuppression> err;
193
0
    ::addinlineSuppressions(tokens, mSettings, suppressions, err);
194
0
    for (std::map<std::string,simplecpp::TokenList*>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
195
0
        if (it->second)
196
0
            ::addinlineSuppressions(*it->second, mSettings, suppressions, err);
197
0
    }
198
0
    for (const BadInlineSuppression &bad : err) {
199
0
        error(bad.location.file(), bad.location.line, bad.errmsg);
200
0
    }
201
0
}
202
203
void Preprocessor::setDirectives(const simplecpp::TokenList &tokens)
204
1.36k
{
205
    // directive list..
206
1.36k
    mDirectives.clear();
207
208
1.36k
    std::vector<const simplecpp::TokenList *> list;
209
1.36k
    list.reserve(1U + mTokenLists.size());
210
1.36k
    list.push_back(&tokens);
211
1.36k
    for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
212
0
        list.push_back(it->second);
213
0
    }
214
215
1.36k
    for (const simplecpp::TokenList *tokenList : list) {
216
81.8k
        for (const simplecpp::Token *tok = tokenList->cfront(); tok; tok = tok->next) {
217
80.4k
            if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line))
218
80.4k
                continue;
219
0
            if (tok->next && tok->next->str() == "endfile")
220
0
                continue;
221
0
            Directive directive(tok->location.file(), tok->location.line, emptyString);
222
0
            for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) {
223
0
                if (tok2->comment)
224
0
                    continue;
225
0
                if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str().size()))
226
0
                    directive.str += ' ';
227
0
                if (directive.str == "#" && tok2->str() == "file")
228
0
                    directive.str += "include";
229
0
                else
230
0
                    directive.str += tok2->str();
231
0
            }
232
0
            mDirectives.push_back(std::move(directive));
233
0
        }
234
1.36k
    }
235
1.36k
}
236
237
static std::string readcondition(const simplecpp::Token *iftok, const std::set<std::string> &defined, const std::set<std::string> &undefined)
238
0
{
239
0
    const simplecpp::Token *cond = iftok->next;
240
0
    if (!sameline(iftok,cond))
241
0
        return "";
242
243
0
    const simplecpp::Token *next1 = cond->next;
244
0
    const simplecpp::Token *next2 = next1 ? next1->next : nullptr;
245
0
    const simplecpp::Token *next3 = next2 ? next2->next : nullptr;
246
247
0
    unsigned int len = 1;
248
0
    if (sameline(iftok,next1))
249
0
        len = 2;
250
0
    if (sameline(iftok,next2))
251
0
        len = 3;
252
0
    if (sameline(iftok,next3))
253
0
        len = 4;
254
255
0
    if (len == 1 && cond->str() == "0")
256
0
        return "0";
257
258
0
    if (len == 1 && cond->name) {
259
0
        if (defined.find(cond->str()) == defined.end())
260
0
            return cond->str();
261
0
    }
262
263
0
    if (len == 2 && cond->op == '!' && next1->name) {
264
0
        if (defined.find(next1->str()) == defined.end())
265
0
            return next1->str() + "=0";
266
0
    }
267
268
0
    if (len == 3 && cond->op == '(' && next1->name && next2->op == ')') {
269
0
        if (defined.find(next1->str()) == defined.end() && undefined.find(next1->str()) == undefined.end())
270
0
            return next1->str();
271
0
    }
272
273
0
    if (len == 3 && cond->name && next1->str() == "==" && next2->number) {
274
0
        if (defined.find(cond->str()) == defined.end())
275
0
            return cond->str() + '=' + cond->next->next->str();
276
0
    }
277
278
0
    std::set<std::string> configset;
279
0
    for (; sameline(iftok,cond); cond = cond->next) {
280
0
        if (cond->op == '!') {
281
0
            if (!sameline(iftok,cond->next) || !cond->next->name)
282
0
                break;
283
0
            if (cond->next->str() == "defined")
284
0
                continue;
285
0
            configset.insert(cond->next->str() + "=0");
286
0
            continue;
287
0
        }
288
0
        if (cond->str() != "defined")
289
0
            continue;
290
0
        const simplecpp::Token *dtok = cond->next;
291
0
        if (!dtok)
292
0
            break;
293
0
        if (dtok->op == '(')
294
0
            dtok = dtok->next;
295
0
        if (sameline(iftok,dtok) && dtok->name && defined.find(dtok->str()) == defined.end() && undefined.find(dtok->str()) == undefined.end())
296
0
            configset.insert(dtok->str());
297
0
    }
298
0
    std::string cfgStr;
299
0
    for (const std::string &s : configset) {
300
0
        if (!cfgStr.empty())
301
0
            cfgStr += ';';
302
0
        cfgStr += s;
303
0
    }
304
0
    return cfgStr;
305
0
}
306
307
static bool hasDefine(const std::string &userDefines, const std::string &cfg)
308
0
{
309
0
    if (cfg.empty()) {
310
0
        return false;
311
0
    }
312
313
0
    std::string::size_type pos = 0;
314
0
    while (pos < userDefines.size()) {
315
0
        pos = userDefines.find(cfg, pos);
316
0
        if (pos == std::string::npos)
317
0
            break;
318
0
        const std::string::size_type pos2 = pos + cfg.size();
319
0
        if ((pos == 0 || userDefines[pos-1U] == ';') && (pos2 == userDefines.size() || userDefines[pos2] == '='))
320
0
            return true;
321
0
        pos = pos2;
322
0
    }
323
0
    return false;
324
0
}
325
326
static std::string cfg(const std::vector<std::string> &configs, const std::string &userDefines)
327
0
{
328
0
    std::set<std::string> configs2(configs.cbegin(), configs.cend());
329
0
    std::string ret;
330
0
    for (const std::string &c : configs2) {
331
0
        if (c.empty())
332
0
            continue;
333
0
        if (c == "0")
334
0
            return "";
335
0
        if (hasDefine(userDefines, c))
336
0
            continue;
337
0
        if (!ret.empty())
338
0
            ret += ';';
339
0
        ret += c;
340
0
    }
341
0
    return ret;
342
0
}
343
344
static bool isUndefined(const std::string &cfg, const std::set<std::string> &undefined)
345
0
{
346
0
    for (std::string::size_type pos1 = 0U; pos1 < cfg.size();) {
347
0
        const std::string::size_type pos2 = cfg.find(';',pos1);
348
0
        const std::string def = (pos2 == std::string::npos) ? cfg.substr(pos1) : cfg.substr(pos1, pos2 - pos1);
349
350
0
        const std::string::size_type eq = def.find('=');
351
0
        if (eq == std::string::npos && undefined.find(def) != undefined.end())
352
0
            return true;
353
0
        if (eq != std::string::npos && undefined.find(def.substr(0,eq)) != undefined.end() && def.substr(eq) != "=0")
354
0
            return true;
355
356
0
        pos1 = (pos2 == std::string::npos) ? pos2 : pos2 + 1U;
357
0
    }
358
0
    return false;
359
0
}
360
361
static bool getConfigsElseIsFalse(const std::vector<std::string> &configs_if, const std::string &userDefines)
362
0
{
363
0
    return std::any_of(configs_if.cbegin(), configs_if.cend(),
364
0
                       [=](const std::string &cfg) {
365
0
        return hasDefine(userDefines, cfg);
366
0
    });
367
0
}
368
369
static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok)
370
0
{
371
0
    int level = 0;
372
0
    while (nullptr != (cmdtok = cmdtok->next)) {
373
0
        if (cmdtok->op == '#' && !sameline(cmdtok->previous,cmdtok) && sameline(cmdtok, cmdtok->next)) {
374
0
            if (startsWith(cmdtok->next->str(),"if"))
375
0
                ++level;
376
0
            else if (cmdtok->next->str() == "endif") {
377
0
                --level;
378
0
                if (level < 0)
379
0
                    return cmdtok;
380
0
            }
381
0
        }
382
0
    }
383
0
    return nullptr;
384
0
}
385
386
static void getConfigs(const simplecpp::TokenList &tokens, std::set<std::string> &defined, const std::string &userDefines, const std::set<std::string> &undefined, std::set<std::string> &ret)
387
1.36k
{
388
1.36k
    std::vector<std::string> configs_if;
389
1.36k
    std::vector<std::string> configs_ifndef;
390
1.36k
    std::string elseError;
391
392
81.8k
    for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) {
393
80.4k
        if (tok->op != '#' || sameline(tok->previous, tok))
394
80.4k
            continue;
395
0
        const simplecpp::Token *cmdtok = tok->next;
396
0
        if (!sameline(tok, cmdtok))
397
0
            continue;
398
0
        if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef" || cmdtok->str() == "if") {
399
0
            std::string config;
400
0
            if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef") {
401
0
                const simplecpp::Token *expr1 = cmdtok->next;
402
0
                if (sameline(tok,expr1) && expr1->name && !sameline(tok,expr1->next))
403
0
                    config = expr1->str();
404
0
                if (defined.find(config) != defined.end())
405
0
                    config.clear();
406
0
            } else if (cmdtok->str() == "if") {
407
0
                config = readcondition(cmdtok, defined, undefined);
408
0
            }
409
410
            // skip undefined configurations..
411
0
            if (isUndefined(config, undefined))
412
0
                config.clear();
413
414
0
            bool ifndef = false;
415
0
            if (cmdtok->str() == "ifndef")
416
0
                ifndef = true;
417
0
            else {
418
0
                const std::array<std::string, 6> match{"if", "!", "defined", "(", config, ")"};
419
0
                int i = 0;
420
0
                ifndef = true;
421
0
                for (const simplecpp::Token *t = cmdtok; i < match.size(); t = t->next) {
422
0
                    if (!t || t->str() != match[i++]) {
423
0
                        ifndef = false;
424
0
                        break;
425
0
                    }
426
0
                }
427
0
            }
428
429
            // include guard..
430
0
            if (ifndef && tok->location.fileIndex > 0) {
431
0
                bool includeGuard = true;
432
0
                for (const simplecpp::Token *t = tok->previous; t; t = t->previous) {
433
0
                    if (t->location.fileIndex == tok->location.fileIndex) {
434
0
                        includeGuard = false;
435
0
                        break;
436
0
                    }
437
0
                }
438
0
                if (includeGuard) {
439
0
                    configs_if.emplace_back(/*std::string()*/);
440
0
                    configs_ifndef.emplace_back(/*std::string()*/);
441
0
                    continue;
442
0
                }
443
0
            }
444
445
0
            configs_if.push_back((cmdtok->str() == "ifndef") ? std::string() : config);
446
0
            configs_ifndef.push_back((cmdtok->str() == "ifndef") ? config : std::string());
447
0
            ret.insert(cfg(configs_if,userDefines));
448
0
        } else if (cmdtok->str() == "elif" || cmdtok->str() == "else") {
449
0
            if (getConfigsElseIsFalse(configs_if,userDefines)) {
450
0
                tok = gotoEndIf(tok);
451
0
                if (!tok)
452
0
                    break;
453
0
                tok = tok->previous;
454
0
                continue;
455
0
            }
456
0
            if (cmdtok->str() == "else" &&
457
0
                cmdtok->next &&
458
0
                !sameline(cmdtok,cmdtok->next) &&
459
0
                sameline(cmdtok->next, cmdtok->next->next) &&
460
0
                cmdtok->next->op == '#' &&
461
0
                cmdtok->next->next->str() == "error") {
462
0
                const std::string &ifcfg = cfg(configs_if, userDefines);
463
0
                if (!ifcfg.empty()) {
464
0
                    if (!elseError.empty())
465
0
                        elseError += ';';
466
0
                    elseError += ifcfg;
467
0
                }
468
0
            }
469
0
            if (!configs_if.empty())
470
0
                configs_if.pop_back();
471
0
            if (cmdtok->str() == "elif") {
472
0
                std::string config = readcondition(cmdtok, defined, undefined);
473
0
                if (isUndefined(config,undefined))
474
0
                    config.clear();
475
0
                configs_if.push_back(std::move(config));
476
0
                ret.insert(cfg(configs_if, userDefines));
477
0
            } else if (!configs_ifndef.empty()) {
478
0
                configs_if.push_back(configs_ifndef.back());
479
0
                ret.insert(cfg(configs_if, userDefines));
480
0
            }
481
0
        } else if (cmdtok->str() == "endif" && !sameline(tok, cmdtok->next)) {
482
0
            if (!configs_if.empty())
483
0
                configs_if.pop_back();
484
0
            if (!configs_ifndef.empty())
485
0
                configs_ifndef.pop_back();
486
0
        } else if (cmdtok->str() == "error") {
487
0
            if (!configs_ifndef.empty() && !configs_ifndef.back().empty()) {
488
0
                if (configs_ifndef.size() == 1U)
489
0
                    ret.erase(emptyString);
490
0
                std::vector<std::string> configs(configs_if);
491
0
                configs.push_back(configs_ifndef.back());
492
0
                ret.erase(cfg(configs, userDefines));
493
0
                std::set<std::string> temp;
494
0
                temp.swap(ret);
495
0
                for (const std::string &c: temp) {
496
0
                    if (c.find(configs_ifndef.back()) != std::string::npos)
497
0
                        ret.insert(c);
498
0
                    else if (c.empty())
499
0
                        ret.insert("");
500
0
                    else
501
0
                        ret.insert(c + ";" + configs_ifndef.back());
502
0
                }
503
0
                if (!elseError.empty())
504
0
                    elseError += ';';
505
0
                elseError += cfg(configs_ifndef, userDefines);
506
0
            }
507
0
            if (!configs_if.empty() && !configs_if.back().empty()) {
508
0
                const std::string &last = configs_if.back();
509
0
                if (last.size() > 2U && last.compare(last.size()-2U,2,"=0") == 0) {
510
0
                    std::vector<std::string> configs(configs_if);
511
0
                    ret.erase(cfg(configs, userDefines));
512
0
                    configs[configs.size() - 1U] = last.substr(0,last.size()-2U);
513
0
                    if (configs.size() == 1U)
514
0
                        ret.erase("");
515
0
                    if (!elseError.empty())
516
0
                        elseError += ';';
517
0
                    elseError += cfg(configs, userDefines);
518
0
                }
519
0
            }
520
0
        } else if (cmdtok->str() == "define" && sameline(tok, cmdtok->next) && cmdtok->next->name) {
521
0
            defined.insert(cmdtok->next->str());
522
0
        }
523
0
    }
524
1.36k
    if (!elseError.empty())
525
0
        ret.insert(std::move(elseError));
526
1.36k
}
527
528
529
std::set<std::string> Preprocessor::getConfigs(const simplecpp::TokenList &tokens) const
530
1.36k
{
531
1.36k
    std::set<std::string> ret = { "" };
532
1.36k
    if (!tokens.cfront())
533
0
        return ret;
534
535
1.36k
    std::set<std::string> defined = { "__cplusplus" };
536
537
1.36k
    ::getConfigs(tokens, defined, mSettings.userDefines, mSettings.userUndefs, ret);
538
539
1.36k
    for (std::map<std::string, simplecpp::TokenList*>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
540
0
        if (!mSettings.configurationExcluded(it->first))
541
0
            ::getConfigs(*(it->second), defined, mSettings.userDefines, mSettings.userUndefs, ret);
542
0
    }
543
544
1.36k
    return ret;
545
1.36k
}
546
547
548
void Preprocessor::preprocess(std::istream &istr, std::map<std::string, std::string> &result, const std::string &filename, const std::list<std::string> &includePaths)
549
0
{
550
0
    (void)includePaths;
551
552
0
    simplecpp::OutputList outputList;
553
0
    std::vector<std::string> files;
554
0
    const simplecpp::TokenList tokens1(istr, files, filename, &outputList);
555
556
0
    const std::set<std::string> configs = getConfigs(tokens1);
557
558
0
    for (const std::string &c : configs) {
559
0
        if (mSettings.userUndefs.find(c) == mSettings.userUndefs.end()) {
560
0
            result[c] = getcode(tokens1, c, files, false);
561
0
        }
562
0
    }
563
0
}
564
565
void Preprocessor::preprocess(std::istream &srcCodeStream, std::string &processedFile, std::list<std::string> &resultConfigurations, const std::string &filename, const std::list<std::string> &includePaths)
566
0
{
567
0
    (void)includePaths;
568
569
0
    if (mFile0.empty())
570
0
        mFile0 = filename;
571
572
0
    simplecpp::OutputList outputList;
573
0
    std::vector<std::string> files;
574
0
    const simplecpp::TokenList tokens1(srcCodeStream, files, filename, &outputList);
575
576
0
    const std::set<std::string> configs = getConfigs(tokens1);
577
0
    std::copy(configs.cbegin(), configs.cend(), std::back_inserter(resultConfigurations));
578
579
0
    processedFile = tokens1.stringify();
580
0
}
581
582
static void splitcfg(const std::string &cfg, std::list<std::string> &defines, const std::string &defaultValue)
583
2.72k
{
584
2.72k
    for (std::string::size_type defineStartPos = 0U; defineStartPos < cfg.size();) {
585
0
        const std::string::size_type defineEndPos = cfg.find(';', defineStartPos);
586
0
        std::string def = (defineEndPos == std::string::npos) ? cfg.substr(defineStartPos) : cfg.substr(defineStartPos, defineEndPos - defineStartPos);
587
0
        if (!defaultValue.empty() && def.find('=') == std::string::npos)
588
0
            def += '=' + defaultValue;
589
0
        defines.push_back(std::move(def));
590
0
        if (defineEndPos == std::string::npos)
591
0
            break;
592
0
        defineStartPos = defineEndPos + 1U;
593
0
    }
594
2.72k
}
595
596
static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename)
597
2.72k
{
598
2.72k
    simplecpp::DUI dui;
599
600
2.72k
    splitcfg(mSettings.userDefines, dui.defines, "1");
601
2.72k
    if (!cfg.empty())
602
0
        splitcfg(cfg, dui.defines, emptyString);
603
604
2.72k
    for (const std::string &def : mSettings.library.defines) {
605
0
        const std::string::size_type pos = def.find_first_of(" (");
606
0
        if (pos == std::string::npos) {
607
0
            dui.defines.push_back(def);
608
0
            continue;
609
0
        }
610
0
        std::string s = def;
611
0
        if (s[pos] == ' ') {
612
0
            s[pos] = '=';
613
0
        } else {
614
0
            s[s.find(')')+1] = '=';
615
0
        }
616
0
        dui.defines.push_back(std::move(s));
617
0
    }
618
619
2.72k
    dui.undefined = mSettings.userUndefs; // -U
620
2.72k
    dui.includePaths = mSettings.includePaths; // -I
621
2.72k
    dui.includes = mSettings.userIncludes;  // --include
622
    // TODO: use mSettings.standards.stdValue instead
623
2.72k
    if (Path::isCPP(filename))
624
2.72k
        dui.std = mSettings.standards.getCPP();
625
0
    else
626
0
        dui.std = mSettings.standards.getC();
627
2.72k
    dui.clearIncludeCache = mSettings.clearIncludeCache;
628
2.72k
    return dui;
629
2.72k
}
630
631
bool Preprocessor::hasErrors(const simplecpp::Output &output)
632
0
{
633
0
    switch (output.type) {
634
0
    case simplecpp::Output::ERROR:
635
0
    case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
636
0
    case simplecpp::Output::SYNTAX_ERROR:
637
0
    case simplecpp::Output::UNHANDLED_CHAR_ERROR:
638
0
    case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND:
639
0
        return true;
640
0
    case simplecpp::Output::WARNING:
641
0
    case simplecpp::Output::MISSING_HEADER:
642
0
    case simplecpp::Output::PORTABILITY_BACKSLASH:
643
0
        break;
644
0
    }
645
0
    return false;
646
0
}
647
648
bool Preprocessor::hasErrors(const simplecpp::OutputList &outputList)
649
1.36k
{
650
1.36k
    const auto it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output) {
651
0
        return hasErrors(output);
652
0
    });
653
1.36k
    return it != outputList.cend();
654
1.36k
}
655
656
void Preprocessor::handleErrors(const simplecpp::OutputList& outputList, bool throwError)
657
2.72k
{
658
2.72k
    const bool showerror = (!mSettings.userDefines.empty() && !mSettings.force);
659
2.72k
    reportOutput(outputList, showerror);
660
2.72k
    if (throwError) {
661
1.36k
        const auto it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output){
662
0
            return hasErrors(output);
663
0
        });
664
1.36k
        if (it != outputList.cend()) {
665
0
            throw *it;
666
0
        }
667
1.36k
    }
668
2.72k
}
669
670
bool Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector<std::string> &files)
671
1.36k
{
672
1.36k
    const simplecpp::DUI dui = createDUI(mSettings, emptyString, files[0]);
673
674
1.36k
    simplecpp::OutputList outputList;
675
1.36k
    mTokenLists = simplecpp::load(rawtokens, files, dui, &outputList);
676
1.36k
    handleErrors(outputList, false);
677
1.36k
    return !hasErrors(outputList);
678
1.36k
}
679
680
void Preprocessor::removeComments()
681
1.36k
{
682
1.36k
    for (std::pair<const std::string, simplecpp::TokenList*>& tokenList : mTokenLists) {
683
0
        if (tokenList.second)
684
0
            tokenList.second->removeComments();
685
0
    }
686
1.36k
}
687
688
void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const
689
1.36k
{
690
1.36k
    tokens->sizeOfType["bool"]          = mSettings.platform.sizeof_bool;
691
1.36k
    tokens->sizeOfType["short"]         = mSettings.platform.sizeof_short;
692
1.36k
    tokens->sizeOfType["int"]           = mSettings.platform.sizeof_int;
693
1.36k
    tokens->sizeOfType["long"]          = mSettings.platform.sizeof_long;
694
1.36k
    tokens->sizeOfType["long long"]     = mSettings.platform.sizeof_long_long;
695
1.36k
    tokens->sizeOfType["float"]         = mSettings.platform.sizeof_float;
696
1.36k
    tokens->sizeOfType["double"]        = mSettings.platform.sizeof_double;
697
1.36k
    tokens->sizeOfType["long double"]   = mSettings.platform.sizeof_long_double;
698
1.36k
    tokens->sizeOfType["bool *"]        = mSettings.platform.sizeof_pointer;
699
1.36k
    tokens->sizeOfType["short *"]       = mSettings.platform.sizeof_pointer;
700
1.36k
    tokens->sizeOfType["int *"]         = mSettings.platform.sizeof_pointer;
701
1.36k
    tokens->sizeOfType["long *"]        = mSettings.platform.sizeof_pointer;
702
1.36k
    tokens->sizeOfType["long long *"]   = mSettings.platform.sizeof_pointer;
703
1.36k
    tokens->sizeOfType["float *"]       = mSettings.platform.sizeof_pointer;
704
1.36k
    tokens->sizeOfType["double *"]      = mSettings.platform.sizeof_pointer;
705
1.36k
    tokens->sizeOfType["long double *"] = mSettings.platform.sizeof_pointer;
706
1.36k
}
707
708
simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, bool throwError)
709
1.36k
{
710
1.36k
    const simplecpp::DUI dui = createDUI(mSettings, cfg, files[0]);
711
712
1.36k
    simplecpp::OutputList outputList;
713
1.36k
    std::list<simplecpp::MacroUsage> macroUsage;
714
1.36k
    std::list<simplecpp::IfCond> ifCond;
715
1.36k
    simplecpp::TokenList tokens2(files);
716
1.36k
    simplecpp::preprocess(tokens2, tokens1, files, mTokenLists, dui, &outputList, &macroUsage, &ifCond);
717
1.36k
    mMacroUsage = macroUsage;
718
1.36k
    mIfCond = ifCond;
719
720
1.36k
    handleErrors(outputList, throwError);
721
722
1.36k
    tokens2.removeComments();
723
724
1.36k
    return tokens2;
725
1.36k
}
726
727
std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector<std::string> &files, const bool writeLocations)
728
0
{
729
0
    simplecpp::TokenList tokens2 = preprocess(tokens1, cfg, files, false);
730
0
    unsigned int prevfile = 0;
731
0
    unsigned int line = 1;
732
0
    std::ostringstream ret;
733
0
    for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) {
734
0
        if (writeLocations && tok->location.fileIndex != prevfile) {
735
0
            ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n";
736
0
            prevfile = tok->location.fileIndex;
737
0
            line = tok->location.line;
738
0
        }
739
740
0
        if (tok->previous && line >= tok->location.line) // #7912
741
0
            ret << ' ';
742
0
        while (tok->location.line > line) {
743
0
            ret << '\n';
744
0
            line++;
745
0
        }
746
0
        if (!tok->macro.empty())
747
0
            ret << Preprocessor::macroChar;
748
0
        ret << tok->str();
749
0
    }
750
751
0
    return ret.str();
752
0
}
753
754
void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror)
755
2.72k
{
756
2.72k
    for (const simplecpp::Output &out : outputList) {
757
0
        switch (out.type) {
758
0
        case simplecpp::Output::ERROR:
759
0
            if (!startsWith(out.msg,"#error") || showerror)
760
0
                error(out.location.file(), out.location.line, out.msg);
761
0
            break;
762
0
        case simplecpp::Output::WARNING:
763
0
        case simplecpp::Output::PORTABILITY_BACKSLASH:
764
0
            break;
765
0
        case simplecpp::Output::MISSING_HEADER: {
766
0
            const std::string::size_type pos1 = out.msg.find_first_of("<\"");
767
0
            const std::string::size_type pos2 = out.msg.find_first_of(">\"", pos1 + 1U);
768
0
            if (pos1 < pos2 && pos2 != std::string::npos)
769
0
                missingInclude(out.location.file(), out.location.line, out.msg.substr(pos1+1, pos2-pos1-1), out.msg[pos1] == '\"' ? UserHeader : SystemHeader);
770
0
        }
771
0
        break;
772
0
        case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY:
773
0
        case simplecpp::Output::SYNTAX_ERROR:
774
0
        case simplecpp::Output::UNHANDLED_CHAR_ERROR:
775
0
            error(out.location.file(), out.location.line, out.msg);
776
0
            break;
777
0
        case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND:
778
0
            error(emptyString, 0, out.msg);
779
0
            break;
780
0
        }
781
0
    }
782
2.72k
}
783
784
void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg)
785
0
{
786
0
    std::list<ErrorMessage::FileLocation> locationList;
787
0
    if (!filename.empty()) {
788
0
        std::string file = Path::fromNativeSeparators(filename);
789
0
        if (mSettings.relativePaths)
790
0
            file = Path::getRelativePath(file, mSettings.basePaths);
791
792
0
        ErrorMessage::FileLocation loc(file, linenr, 0);
793
0
        locationList.push_back(std::move(loc));
794
0
    }
795
0
    mErrorLogger->reportErr(ErrorMessage(locationList,
796
0
                                         mFile0,
797
0
                                         Severity::error,
798
0
                                         msg,
799
0
                                         "preprocessorErrorDirective",
800
0
                                         Certainty::normal));
801
0
}
802
803
// Report that include is missing
804
void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType)
805
0
{
806
0
    if (!mSettings.checks.isEnabled(Checks::missingInclude) || !mErrorLogger)
807
0
        return;
808
809
0
    std::list<ErrorMessage::FileLocation> locationList;
810
0
    if (!filename.empty()) {
811
0
        locationList.emplace_back(filename, linenr);
812
0
    }
813
0
    ErrorMessage errmsg(std::move(locationList), mFile0, Severity::information,
814
0
                        (headerType==SystemHeader) ?
815
0
                        "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." :
816
0
                        "Include file: \"" + header + "\" not found.",
817
0
                        (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude",
818
0
                        Certainty::normal);
819
0
    mErrorLogger->reportErr(errmsg);
820
0
}
821
822
void Preprocessor::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings)
823
0
{
824
0
    Settings settings2(*settings);
825
0
    Preprocessor preprocessor(settings2, errorLogger);
826
0
    preprocessor.missingInclude(emptyString, 1, emptyString, UserHeader);
827
0
    preprocessor.missingInclude(emptyString, 1, emptyString, SystemHeader);
828
0
    preprocessor.error(emptyString, 1, "#error message");   // #error ..
829
0
}
830
831
void Preprocessor::dump(std::ostream &out) const
832
0
{
833
    // Create a xml dump.
834
835
0
    out << "  <directivelist>" << std::endl;
836
0
    for (const Directive &dir : mDirectives) {
837
0
        out << "    <directive "
838
0
            << "file=\"" << ErrorLogger::toxml(dir.file) << "\" "
839
0
            << "linenr=\"" << dir.linenr << "\" "
840
            // str might contain characters such as '"', '<' or '>' which
841
            // could result in invalid XML, so run it through toxml().
842
0
            << "str=\"" << ErrorLogger::toxml(dir.str) << "\"/>" << std::endl;
843
0
    }
844
0
    out << "  </directivelist>" << std::endl;
845
846
0
    if (!mMacroUsage.empty()) {
847
0
        out << "  <macro-usage>" << std::endl;
848
0
        for (const simplecpp::MacroUsage &macroUsage: mMacroUsage) {
849
0
            out << "    <macro"
850
0
                << " name=\"" << macroUsage.macroName << "\""
851
0
                << " file=\"" << ErrorLogger::toxml(macroUsage.macroLocation.file()) << "\""
852
0
                << " line=\"" << macroUsage.macroLocation.line << "\""
853
0
                << " column=\"" << macroUsage.macroLocation.col << "\""
854
0
                << " usefile=\"" << ErrorLogger::toxml(macroUsage.useLocation.file()) << "\""
855
0
                << " useline=\"" << macroUsage.useLocation.line << "\""
856
0
                << " usecolumn=\"" << macroUsage.useLocation.col << "\""
857
0
                << " is-known-value=\"" << bool_to_string(macroUsage.macroValueKnown) << "\""
858
0
                << "/>" << std::endl;
859
0
        }
860
0
        out << "  </macro-usage>" << std::endl;
861
0
    }
862
863
0
    if (!mIfCond.empty()) {
864
0
        out << "  <simplecpp-if-cond>" << std::endl;
865
0
        for (const simplecpp::IfCond &ifCond: mIfCond) {
866
0
            out << "    <if-cond"
867
0
                << " file=\"" << ErrorLogger::toxml(ifCond.location.file()) << "\""
868
0
                << " line=\"" << ifCond.location.line << "\""
869
0
                << " column=\"" << ifCond.location.col << "\""
870
0
                << " E=\"" << ErrorLogger::toxml(ifCond.E) << "\""
871
0
                << " result=\"" << ifCond.result << "\""
872
0
                << "/>" << std::endl;
873
0
        }
874
0
        out << "  </simplecpp-if-cond>" << std::endl;
875
0
    }
876
0
}
877
878
std::size_t Preprocessor::calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
879
0
{
880
0
    std::string hashData = toolinfo;
881
0
    for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
882
0
        if (!tok->comment)
883
0
            hashData += tok->str();
884
0
    }
885
0
    for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) {
886
0
        for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) {
887
0
            if (!tok->comment)
888
0
                hashData += tok->str();
889
0
        }
890
0
    }
891
0
    return (std::hash<std::string>{})(hashData);
892
0
}
893
894
void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList) const
895
1.36k
{
896
1.36k
    Preprocessor::simplifyPragmaAsmPrivate(tokenList);
897
1.36k
    for (const std::pair<const std::string, simplecpp::TokenList*>& list : mTokenLists) {
898
0
        Preprocessor::simplifyPragmaAsmPrivate(list.second);
899
0
    }
900
1.36k
}
901
902
void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList)
903
1.36k
{
904
    // assembler code..
905
81.8k
    for (simplecpp::Token *tok = tokenList->front(); tok; tok = tok->next) {
906
80.4k
        if (tok->op != '#')
907
80.4k
            continue;
908
0
        if (sameline(tok, tok->previousSkipComments()))
909
0
            continue;
910
911
0
        const simplecpp::Token * const tok2 = tok->nextSkipComments();
912
0
        if (!tok2 || !sameline(tok, tok2) || tok2->str() != "pragma")
913
0
            continue;
914
915
0
        const simplecpp::Token * const tok3 = tok2->nextSkipComments();
916
0
        if (!tok3 || !sameline(tok, tok3) || tok3->str() != "asm")
917
0
            continue;
918
919
0
        const simplecpp::Token *endasm = tok3;
920
0
        while ((endasm = endasm->next) != nullptr) {
921
0
            if (endasm->op != '#' || sameline(endasm,endasm->previousSkipComments()))
922
0
                continue;
923
0
            const simplecpp::Token * const endasm2 = endasm->nextSkipComments();
924
0
            if (!endasm2 || !sameline(endasm, endasm2) || endasm2->str() != "pragma")
925
0
                continue;
926
0
            const simplecpp::Token * const endasm3 = endasm2->nextSkipComments();
927
0
            if (!endasm3 || !sameline(endasm2, endasm3) || endasm3->str() != "endasm")
928
0
                continue;
929
0
            while (sameline(endasm,endasm3))
930
0
                endasm = endasm->next;
931
0
            break;
932
0
        }
933
934
0
        const simplecpp::Token * const tok4 = tok3->next;
935
0
        tok->setstr("asm");
936
0
        const_cast<simplecpp::Token *>(tok2)->setstr("(");
937
0
        const_cast<simplecpp::Token *>(tok3)->setstr(")");
938
0
        const_cast<simplecpp::Token *>(tok4)->setstr(";");
939
0
        while (tok4->next != endasm)
940
0
            tokenList->deleteToken(tok4->next);
941
0
    }
942
1.36k
}