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