Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | /// @file compiler/Logger.cc | ||
5 | |||
6 | #include "Logger.h" | ||
7 | |||
8 | #include <stack> | ||
9 | |||
10 | namespace openvdb { | ||
11 | OPENVDB_USE_VERSION_NAMESPACE | ||
12 | namespace OPENVDB_VERSION_NAME { | ||
13 | |||
14 | namespace ax { | ||
15 | |||
16 | 4682 | struct Logger::Settings | |
17 | { | ||
18 | size_t mMaxErrors = 0; | ||
19 | bool mWarningsAsErrors = false; | ||
20 | // message formatting settings | ||
21 | bool mNumbered = true; | ||
22 | const char* mErrorPrefix = "error: "; | ||
23 | const char* mWarningPrefix = "warning: "; | ||
24 | bool mPrintLines = false; | ||
25 | std::string mIndent; | ||
26 | }; | ||
27 | |||
28 | |||
29 | /// @brief Wrapper around a code snippet to print individual lines from a multi | ||
30 | /// line string | ||
31 | /// @note Assumes a null terminated c-style string input | ||
32 | 3380 | struct Logger::SourceCode | |
33 | { | ||
34 | SourceCode(const char* string = nullptr) | ||
35 | 3380 | : mString(string) | |
36 | , mOffsets() | ||
37 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3380 times.
|
3380 | , mLines() { |
38 | reset(string); | ||
39 | } | ||
40 | |||
41 | /// @brief Print a line of the multi-line string to the stream | ||
42 | /// @note If no string hs been provided, will do nothing | ||
43 | /// @param line Line number to print | ||
44 | /// @param os Output stream | ||
45 | ✗ | void getLine(const size_t num, std::ostream* os) | |
46 | { | ||
47 | ✗ | if (num < 1) return; | |
48 | ✗ | if (mOffsets.empty()) getLineOffsets(); | |
49 | ✗ | if (num > mLines) return; | |
50 | ✗ | const size_t start = mOffsets[num - 1]; | |
51 | ✗ | const size_t end = mOffsets[num]; | |
52 | ✗ | for (size_t i = start; i < end - 1; ++i) *os << mString[i]; | |
53 | } | ||
54 | |||
55 | void reset(const char* string) | ||
56 | { | ||
57 | mString = string; | ||
58 | mOffsets.clear(); | ||
59 | mLines = 0; | ||
60 | } | ||
61 | |||
62 | bool hasString() const { return static_cast<bool>(mString); } | ||
63 | |||
64 | private: | ||
65 | |||
66 | ✗ | void getLineOffsets() | |
67 | { | ||
68 | ✗ | if (!mString) return; | |
69 | ✗ | mOffsets.emplace_back(0); | |
70 | ✗ | size_t offset = 1; | |
71 | ✗ | const char* iter = mString; | |
72 | ✗ | while (*iter != '\0') { | |
73 | ✗ | if (*iter == '\n') mOffsets.emplace_back(offset); | |
74 | ✗ | ++iter; ++offset; | |
75 | } | ||
76 | ✗ | mOffsets.emplace_back(offset); | |
77 | ✗ | mLines = mOffsets.size(); | |
78 | } | ||
79 | |||
80 | const char* mString; | ||
81 | std::vector<size_t> mOffsets; | ||
82 | size_t mLines; | ||
83 | }; | ||
84 | |||
85 | namespace { | ||
86 | |||
87 | /// @brief Return a stack denoting the position in the tree of this node | ||
88 | /// Where each node is represented by its childidx of its parent | ||
89 | /// This gives a branching path to follow to reach this node from the | ||
90 | /// tree root | ||
91 | /// @parm node Node pointer to create position stack for | ||
92 | 1857 | inline std::stack<size_t> pathStackFromNode(const ast::Node* node) | |
93 | { | ||
94 | std::stack<size_t> path; | ||
95 | const ast::Node* child = node; | ||
96 | const ast::Node* parent = node->parent(); | ||
97 |
2/2✓ Branch 0 taken 4558 times.
✓ Branch 1 taken 1857 times.
|
6415 | while (parent) { |
98 |
2/4✓ Branch 1 taken 4558 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4558 times.
✗ Branch 5 not taken.
|
4558 | path.emplace(child->childidx()); |
99 | child = parent; | ||
100 | parent = child->parent(); | ||
101 | } | ||
102 | 1857 | return path; | |
103 | } | ||
104 | |||
105 | /// @brief Iterate through a tree, following the branch numbers from the path | ||
106 | /// stack, returning a Node* to the node at this position | ||
107 | /// @parm path Stack of child branches to follow | ||
108 | /// @parm tree Tree containing node to return | ||
109 | 1857 | inline const ast::Node* nodeFromPathStack(std::stack<size_t>& path, | |
110 | const ast::Tree& tree) | ||
111 | { | ||
112 | 1857 | const ast::Node* node = &tree; | |
113 |
1/2✓ Branch 0 taken 6415 times.
✗ Branch 1 not taken.
|
6415 | while (node) { |
114 |
2/2✓ Branch 0 taken 4558 times.
✓ Branch 1 taken 1857 times.
|
6415 | if (path.empty()) return node; |
115 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4558 times.
|
4558 | node = node->child(path.top()); |
116 | path.pop(); | ||
117 | } | ||
118 | return nullptr; | ||
119 | } | ||
120 | |||
121 | /// @brief Given any node and a tree and node to location map, return the line | ||
122 | /// and column number for the nodes equivalent (in terms of position in the | ||
123 | /// tree) from the supplied tree | ||
124 | /// @note Requires the map to have been populated for all nodes in the supplied | ||
125 | /// tree, otherwise will return 0:0 | ||
126 | inline const Logger::CodeLocation | ||
127 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1857 times.
|
1865 | nodeToCodeLocation(const ast::Node* node, |
128 | const ast::Tree::ConstPtr tree, | ||
129 | const std::unordered_map | ||
130 | <const ax::ast::Node*, Logger::CodeLocation>& map) | ||
131 | { | ||
132 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1857 times.
|
1865 | if (!tree) return Logger::CodeLocation(0,0); |
133 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1857 times.
|
1857 | assert(node); |
134 | 1857 | std::stack<size_t> pathStack = pathStackFromNode(node); | |
135 |
1/2✓ Branch 1 taken 1857 times.
✗ Branch 2 not taken.
|
1857 | const ast::Node* nodeInMap = nodeFromPathStack(pathStack, *tree); |
136 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1857 times.
|
1857 | const auto locationIter = map.find(nodeInMap); |
137 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1857 times.
|
1857 | if (locationIter == map.end()) return Logger::CodeLocation(0,0); |
138 | 1857 | return locationIter->second; | |
139 | } | ||
140 | |||
141 | 2459 | std::string format(const std::string& message, | |
142 | const Logger::CodeLocation& loc, | ||
143 | const size_t numMessage, | ||
144 | const bool numbered, | ||
145 | const bool printLines, | ||
146 | const std::string& indent, | ||
147 | Logger::SourceCode* sourceCode) | ||
148 | { | ||
149 | 4918 | std::stringstream ss; | |
150 | ss << indent; | ||
151 |
2/4✓ Branch 0 taken 2459 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 2459 times.
✗ Branch 4 not taken.
|
4918 | if (numbered) ss << "[" << numMessage << "] "; |
152 |
3/4✓ Branch 0 taken 122167 times.
✓ Branch 1 taken 2459 times.
✓ Branch 3 taken 122167 times.
✗ Branch 4 not taken.
|
124626 | for (auto c : message) { |
153 | 122167 | ss << c; | |
154 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 122167 times.
|
122167 | if (c == '\n') ss << indent; |
155 | } | ||
156 |
2/2✓ Branch 0 taken 2446 times.
✓ Branch 1 taken 13 times.
|
2459 | if (loc.first > 0) { |
157 |
2/4✓ Branch 1 taken 2446 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2446 times.
✗ Branch 5 not taken.
|
4892 | ss << " " << loc.first << ":" << loc.second; |
158 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2446 times.
|
2446 | if (printLines && sourceCode) { |
159 | ✗ | ss << '\n' << indent; | |
160 | ✗ | sourceCode->getLine(loc.first, &ss); | |
161 | ✗ | ss << '\n' << indent; | |
162 | ✗ | for (size_t i = 0; i < loc.second - 1; ++i) ss << '-'; | |
163 | ✗ | ss << '^'; | |
164 | } | ||
165 | } | ||
166 | 2459 | return ss.str(); | |
167 | } | ||
168 | |||
169 | } | ||
170 | |||
171 | 2341 | Logger::Logger(const Logger::OutputFunction& errors, | |
172 | 2341 | const Logger::OutputFunction& warnings) | |
173 | : mErrorOutput(errors) | ||
174 | , mWarningOutput(warnings) | ||
175 | , mNumErrors(0) | ||
176 | , mNumWarnings(0) | ||
177 | 2341 | , mSettings(new Logger::Settings()) | |
178 |
2/4✓ Branch 2 taken 2341 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2341 times.
✗ Branch 6 not taken.
|
2341 | , mCode() {} |
179 | |||
180 | 2341 | Logger::~Logger() {} | |
181 | |||
182 | 3380 | void Logger::setSourceCode(const char* code) | |
183 | { | ||
184 | 3380 | mCode.reset(new SourceCode(code)); | |
185 | 3380 | } | |
186 | |||
187 | 892 | bool Logger::error(const std::string& message, | |
188 | const Logger::CodeLocation& lineCol) | ||
189 | { | ||
190 | // check if we've already exceeded the error limit | ||
191 | 892 | const bool limit = this->atErrorLimit(); | |
192 | // Always increment the error counter | ||
193 | 892 | ++mNumErrors; | |
194 |
2/2✓ Branch 0 taken 891 times.
✓ Branch 1 taken 1 times.
|
892 | if (limit) return false; |
195 |
3/4✓ Branch 4 taken 891 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 886 times.
✓ Branch 8 taken 5 times.
|
2673 | mErrorOutput(format(this->getErrorPrefix() + message, |
196 | lineCol, | ||
197 | this->errors(), | ||
198 | 891 | this->getNumberedOutput(), | |
199 | 891 | this->getPrintLines(), | |
200 | 891 | this->mSettings->mIndent, | |
201 | this->mCode.get())); | ||
202 | 886 | return !this->atErrorLimit(); | |
203 | } | ||
204 | |||
205 | 315 | bool Logger::error(const std::string& message, | |
206 | const ax::ast::Node* node) | ||
207 | { | ||
208 |
4/8✓ Branch 2 taken 315 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 315 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 310 times.
✓ Branch 8 taken 5 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
625 | return this->error(message, nodeToCodeLocation(node, mTreePtr, mNodeToLineColMap)); |
209 | } | ||
210 | |||
211 | 1569 | bool Logger::warning(const std::string& message, | |
212 | const Logger::CodeLocation& lineCol) | ||
213 | { | ||
214 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1568 times.
|
1569 | if (this->getWarningsAsErrors()) { |
215 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | return this->error(message + " [warning-as-error]", lineCol); |
216 | } | ||
217 | else { | ||
218 | 1568 | ++mNumWarnings; | |
219 |
2/4✓ Branch 4 taken 1568 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1568 times.
✗ Branch 8 not taken.
|
4704 | mWarningOutput(format(this->getWarningPrefix() + message, |
220 | lineCol, | ||
221 | this->warnings(), | ||
222 | 1568 | this->getNumberedOutput(), | |
223 | 1568 | this->getPrintLines(), | |
224 | 1568 | this->mSettings->mIndent, | |
225 | this->mCode.get())); | ||
226 | 1568 | return true; | |
227 | } | ||
228 | } | ||
229 | |||
230 | 1550 | bool Logger::warning(const std::string& message, | |
231 | const ax::ast::Node* node) | ||
232 | { | ||
233 |
4/8✓ Branch 2 taken 1550 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1550 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1547 times.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
3097 | return this->warning(message, nodeToCodeLocation(node, mTreePtr, mNodeToLineColMap)); |
234 | } | ||
235 | |||
236 | 1 | void Logger::setWarningsAsErrors(const bool warnAsError) | |
237 | { | ||
238 | 1 | mSettings->mWarningsAsErrors = warnAsError; | |
239 | 1 | } | |
240 | |||
241 | 1569 | bool Logger::getWarningsAsErrors() const | |
242 | { | ||
243 | 1569 | return mSettings->mWarningsAsErrors; | |
244 | } | ||
245 | |||
246 | 2 | void Logger::setMaxErrors(const size_t maxErrors) | |
247 | { | ||
248 | 2 | mSettings->mMaxErrors = maxErrors; | |
249 | 2 | } | |
250 | |||
251 | 3102 | size_t Logger::getMaxErrors() const | |
252 | { | ||
253 | 3102 | return mSettings->mMaxErrors; | |
254 | } | ||
255 | |||
256 | ✗ | void Logger::setNumberedOutput(const bool numbered) | |
257 | { | ||
258 | ✗ | mSettings->mNumbered = numbered; | |
259 | } | ||
260 | |||
261 | ✗ | void Logger::setIndent(const size_t ident) | |
262 | { | ||
263 | ✗ | mSettings->mIndent = std::string(ident, ' '); | |
264 | } | ||
265 | |||
266 | ✗ | void Logger::setErrorPrefix(const char* prefix) | |
267 | { | ||
268 | ✗ | mSettings->mErrorPrefix = prefix; | |
269 | } | ||
270 | |||
271 | ✗ | void Logger::setWarningPrefix(const char* prefix) | |
272 | { | ||
273 | ✗ | mSettings->mWarningPrefix = prefix; | |
274 | } | ||
275 | |||
276 | ✗ | void Logger::setPrintLines(const bool print) | |
277 | { | ||
278 | ✗ | mSettings->mPrintLines = print; | |
279 | } | ||
280 | |||
281 | 2459 | bool Logger::getNumberedOutput() const | |
282 | { | ||
283 | 2459 | return mSettings->mNumbered; | |
284 | } | ||
285 | |||
286 | ✗ | size_t Logger::getIndent() const | |
287 | { | ||
288 | ✗ | return mSettings->mIndent.size(); | |
289 | } | ||
290 | |||
291 | 891 | const char* Logger::getErrorPrefix() const | |
292 | { | ||
293 | 891 | return mSettings->mErrorPrefix; | |
294 | } | ||
295 | |||
296 | 1568 | const char* Logger::getWarningPrefix() const | |
297 | { | ||
298 | 1568 | return mSettings->mWarningPrefix; | |
299 | } | ||
300 | |||
301 | 2459 | bool Logger::getPrintLines() const | |
302 | { | ||
303 | 2459 | return mSettings->mPrintLines; | |
304 | } | ||
305 | |||
306 |
2/2✓ Branch 0 taken 2551 times.
✓ Branch 1 taken 459 times.
|
3010 | void Logger::clear() |
307 | { | ||
308 | mCode.reset(); | ||
309 | 3010 | mNumErrors = 0; | |
310 | 3010 | mNumWarnings = 0; | |
311 | mNodeToLineColMap.clear(); | ||
312 | mTreePtr = nullptr; | ||
313 | 3010 | } | |
314 | |||
315 | 3380 | void Logger::setSourceTree(openvdb::ax::ast::Tree::ConstPtr tree) | |
316 | { | ||
317 | mTreePtr = tree; | ||
318 | 3380 | } | |
319 | |||
320 | 101110 | void Logger::addNodeLocation(const ax::ast::Node* node, const Logger::CodeLocation& location) | |
321 | { | ||
322 | mNodeToLineColMap.emplace(node, location); | ||
323 | 101110 | } | |
324 | |||
325 | } // namespace ax | ||
326 | } // namespace OPENVDB_VERSION_NAME | ||
327 | } // namespace openvdb | ||
328 | |||
329 |