/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 | } |