Coverage Report

Created: 2023-09-25 06:15

/src/cppcheck/lib/pathanalysis.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
#include "pathanalysis.h"
20
21
#include "astutils.h"
22
#include "symboldatabase.h"
23
#include "token.h"
24
#include "vfvalue.h"
25
26
#include <algorithm>
27
#include <string>
28
#include <tuple>
29
#include <type_traits>
30
31
const Scope* PathAnalysis::findOuterScope(const Scope * scope)
32
0
{
33
0
    if (!scope)
34
0
        return nullptr;
35
0
    if (scope->isLocal() && scope->type != Scope::eSwitch)
36
0
        return findOuterScope(scope->nestedIn);
37
0
    return scope;
38
0
}
39
40
static const Token* assignExpr(const Token* tok)
41
0
{
42
0
    while (tok->astParent() && astIsLHS(tok)) {
43
0
        if (Token::Match(tok->astParent(), "%assign%"))
44
0
            return tok->astParent();
45
0
        tok = tok->astParent();
46
0
    }
47
0
    return nullptr;
48
0
}
49
50
std::pair<bool, bool> PathAnalysis::checkCond(const Token * tok, bool& known)
51
0
{
52
0
    if (tok->hasKnownIntValue()) {
53
0
        known = true;
54
0
        return std::make_pair(tok->values().front().intvalue, !tok->values().front().intvalue);
55
0
    }
56
0
    auto it = std::find_if(tok->values().cbegin(), tok->values().cend(), [](const ValueFlow::Value& v) {
57
0
        return v.isIntValue();
58
0
    });
59
    // If all possible values are the same, then assume all paths have the same value
60
0
    if (it != tok->values().cend() && std::all_of(it, tok->values().cend(), [&](const ValueFlow::Value& v) {
61
0
        if (v.isIntValue())
62
0
            return v.intvalue == it->intvalue;
63
0
        return true;
64
0
    })) {
65
0
        known = false;
66
0
        return std::make_pair(it->intvalue, !it->intvalue);
67
0
    }
68
0
    return std::make_pair(true, true);
69
0
}
70
71
PathAnalysis::Progress PathAnalysis::forwardRecursive(const Token* tok, Info info, const std::function<PathAnalysis::Progress(const Info&)>& f)
72
0
{
73
0
    if (!tok)
74
0
        return Progress::Continue;
75
0
    if (tok->astOperand1() && forwardRecursive(tok->astOperand1(), info, f) == Progress::Break)
76
0
        return Progress::Break;
77
0
    info.tok = tok;
78
0
    if (f(info) == Progress::Break)
79
0
        return Progress::Break;
80
0
    if (tok->astOperand2() && forwardRecursive(tok->astOperand2(), info, f) == Progress::Break)
81
0
        return Progress::Break;
82
0
    return Progress::Continue;
83
0
}
84
85
PathAnalysis::Progress PathAnalysis::forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function<PathAnalysis::Progress(const Info&)>& f) const
86
0
{
87
0
    for (const Token *tok = startToken; precedes(tok, endToken); tok = tok->next()) {
88
0
        if (Token::Match(tok, "asm|goto|break|continue"))
89
0
            return Progress::Break;
90
0
        if (Token::Match(tok, "return|throw")) {
91
0
            forwardRecursive(tok, info, f);
92
0
            return Progress::Break;
93
            // Evaluate RHS of assignment before LHS
94
0
        }
95
0
        if (const Token* assignTok = assignExpr(tok)) {
96
0
            if (forwardRecursive(assignTok->astOperand2(), info, f) == Progress::Break)
97
0
                return Progress::Break;
98
0
            if (forwardRecursive(assignTok->astOperand1(), info, f) == Progress::Break)
99
0
                return Progress::Break;
100
0
            tok = nextAfterAstRightmostLeaf(assignTok);
101
0
            if (!tok)
102
0
                return Progress::Break;
103
0
        } else if (Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), ") {") && Token::Match(tok->link()->linkAt(-1)->previous(), "if|while|for (")) {
104
0
            const Token * blockStart = tok->link()->linkAt(-1)->previous();
105
0
            const Token * condTok = getCondTok(blockStart);
106
0
            if (!condTok)
107
0
                continue;
108
0
            info.errorPath.emplace_back(condTok, "Assuming condition is true.");
109
            // Traverse a loop a second time
110
0
            if (Token::Match(blockStart, "for|while (")) {
111
0
                const Token* endCond = blockStart->linkAt(1);
112
0
                bool traverseLoop = true;
113
                // Only traverse simple for loops
114
0
                if (Token::simpleMatch(blockStart, "for") && !Token::Match(endCond->tokAt(-3), "; ++|--|%var% %var%|++|-- ) {"))
115
0
                    traverseLoop = false;
116
                // Traverse loop a second time
117
0
                if (traverseLoop) {
118
                    // Traverse condition
119
0
                    if (forwardRecursive(condTok, info, f) == Progress::Break)
120
0
                        return Progress::Break;
121
                    // TODO: Should we traverse the body: forwardRange(tok->link(), tok, info, f)?
122
0
                }
123
0
            }
124
0
            if (Token::simpleMatch(tok, "} else {")) {
125
0
                tok = tok->linkAt(2);
126
0
            }
127
0
        } else if (Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) {
128
0
            const Token * endCond = tok->next()->link();
129
0
            const Token * endBlock = endCond->next()->link();
130
0
            const Token * condTok = getCondTok(tok);
131
0
            if (!condTok)
132
0
                continue;
133
            // Traverse condition
134
0
            if (forwardRange(tok->next(), tok->next()->link(), info, f) == Progress::Break)
135
0
                return Progress::Break;
136
0
            Info i = info;
137
0
            i.known = false;
138
0
            i.errorPath.emplace_back(condTok, "Assuming condition is true.");
139
140
            // Check if condition is true or false
141
0
            bool checkThen = false;
142
0
            bool checkElse = false;
143
0
            std::tie(checkThen, checkElse) = checkCond(condTok, i.known);
144
145
            // Traverse then block
146
0
            if (checkThen) {
147
0
                if (forwardRange(endCond->next(), endBlock, i, f) == Progress::Break)
148
0
                    return Progress::Break;
149
0
            }
150
            // Traverse else block
151
0
            if (Token::simpleMatch(endBlock, "} else {")) {
152
0
                if (checkElse) {
153
0
                    i.errorPath.back().second = "Assuming condition is false.";
154
0
                    const Progress result = forwardRange(endCond->next(), endBlock, i, f);
155
0
                    if (result == Progress::Break)
156
0
                        return Progress::Break;
157
0
                }
158
0
                tok = endBlock->linkAt(2);
159
0
            } else {
160
0
                tok = endBlock;
161
0
            }
162
0
        } else if (Token::simpleMatch(tok, "} else {")) {
163
0
            tok = tok->linkAt(2);
164
0
        } else {
165
0
            info.tok = tok;
166
0
            if (f(info) == Progress::Break)
167
0
                return Progress::Break;
168
0
        }
169
        // Prevent infinite recursion
170
0
        if (tok->next() == start)
171
0
            break;
172
0
    }
173
0
    return Progress::Continue;
174
0
}
175
176
void PathAnalysis::forward(const std::function<Progress(const Info&)>& f) const
177
0
{
178
0
    const Scope * endScope = findOuterScope(start->scope());
179
0
    if (!endScope)
180
0
        return;
181
0
    const Token * endToken = endScope->bodyEnd;
182
0
    Info info{start, ErrorPath{}, true};
183
0
    forwardRange(start, endToken, info, f);
184
0
}
185
186
bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath)
187
0
{
188
0
    PathAnalysis::Info info = PathAnalysis{start, library}.forwardFind([&](const PathAnalysis::Info& i) {
189
0
        return (i.tok == dest);
190
0
    });
191
0
    if (!info.tok)
192
0
        return false;
193
0
    if (errorPath)
194
0
        errorPath->insert(errorPath->end(), info.errorPath.cbegin(), info.errorPath.cend());
195
0
    return true;
196
0
}