1 /* 2 SPDX-FileCopyrightText: 2006 David Nolden <david.nolden.kdevelop@art-master.de> 3 SPDX-License-Identifier: GPL-2.0-or-later 4 */ 5 6 #ifndef MACRO_H 7 #define MACRO_H 8 9 #include <qpair.h> 10 #include <QStringList> 11 #include <QString> 12 #include <qdatastream.h> 13 #include <qmap.h> 14 #include <qdatetime.h> 15 #include <map> 16 #include <set> 17 #include <hashedstring.h> 18 #include <kdatastream.h> 19 #include <codemodel.h> 20 21 typedef signed char Q_INT8; 22 23 //This files should be renamed to something like "helpers.h" 24 25 /** 26 * Encapsulates a problem in a piece of source code. 27 */ 28 class Problem 29 { 30 public: 31 enum { 32 Level_Error = 0, ///< Indicates an error that will prevent the code from compiling 33 Level_Warning, ///< Indicates a warning 34 Level_Todo, ///< Indicates there is still something left to do 35 Level_Fixme ///< Indicates that something needs to be fixed 36 }; 37 38 public: Problem()39 Problem() {} Problem(const Problem & source)40 Problem(const Problem& source) 41 : m_text(source.m_text), m_line(source.m_line), 42 m_column(source.m_column), m_level(source.m_level), m_file(source.m_file) {} 43 Problem(const QString& text, int line, int column, int level = Level_Error) m_text(text)44 : m_text(text), m_line(line), m_column(column), m_level(level) {} 45 Problem(const Problem & source,bool)46 Problem(const Problem& source, bool /*threadSafeClone*/) 47 : m_text(QString::fromUtf8(source.m_text.toUtf8().data())), m_line(source.m_line), 48 m_column(source.m_column), m_level(source.m_level), m_file(QString::fromUtf8(source.m_file.toUtf8().data())) {} 49 50 Problem& operator = (const Problem& source) 51 { 52 m_text = source.m_text; 53 m_line = source.m_line; 54 m_column = source.m_column; 55 m_level = source.m_level; 56 m_file = source.m_file; 57 return (*this); 58 } 59 setFileName(const QString & fileName)60 void setFileName(const QString& fileName) 61 { 62 m_file = fileName; 63 } 64 65 bool operator == (const Problem& p) const 66 { 67 return m_text == p.m_text && m_line == p.m_line && m_column == p.m_column && m_level == p.m_level && m_file == p.m_file; 68 } 69 70 /** Get the filename in which the problem was encountered */ fileName()71 QString fileName() const 72 { 73 return m_file; 74 } 75 76 /** Get the text for the problem */ text()77 QString text() const 78 { 79 return m_text; 80 } 81 /** Get the line number of the problem */ line()82 int line() const 83 { 84 return m_line; 85 } 86 /** Get the column of the problem */ column()87 int column() const 88 { 89 return m_column; 90 } 91 /** 92 * Get the seriousness of the problem. There are four possibilities: 93 * \li Error 94 * \li Warning 95 * \li Todo 96 * \li Fixme 97 */ level()98 int level() const 99 { 100 return m_level; 101 } 102 103 private: 104 QString m_text; 105 int m_line; 106 int m_column; 107 int m_level; 108 QString m_file; 109 }; 110 111 112 /** 113 * A datatype that represents a preprocessor macro. 114 * Most of the functions in this class need to be inline, so we do not have to import cppparser to many modules. The other solution would be moving macro into interfaces. 115 */ 116 class Macro 117 { 118 public: 119 typedef QString Argument; 120 121 public: 122 explicit Macro(bool hasArguments = false) m_idHashValid(false)123 : m_idHashValid(false), 124 m_valueHashValid(false), 125 m_idHash(0), 126 m_valueHash(0), 127 m_line(0), 128 m_column(0), 129 m_hasArguments(hasArguments), 130 m_isUndefMacro(false) 131 {} Macro(const QString & n,const QString & b)132 Macro(const QString &n, const QString &b) 133 : m_idHashValid(false), 134 m_valueHashValid(false), 135 m_idHash(0), 136 m_valueHash(0), 137 m_name(n), 138 m_line(0), 139 m_column(0), 140 m_body(b), 141 m_hasArguments(false), 142 m_isUndefMacro(false) 143 {} 144 145 //Sorts the macros by their hash-value, then by their name. 146 class NameArgCompare { 147 public: operator()148 bool operator () (const Macro& lhs, const Macro& rhs) const 149 { 150 size_t lhash = lhs.idHash(); 151 size_t rhash = rhs.idHash(); 152 if (lhash < rhash) return true; 153 else if (lhash > rhash) return false; 154 155 int df = lhs.m_name.compare(rhs.m_name); 156 if (df < 0) 157 return true; 158 if (df == 0) { 159 if (!lhs.m_hasArguments && rhs.m_hasArguments) { 160 return true; 161 } else if (lhs.m_hasArguments == rhs.m_hasArguments) { 162 return lhs.m_argumentList.count() < rhs.m_argumentList.count(); 163 164 } else { 165 return false; 166 } 167 } 168 return false; 169 } 170 }; 171 class NameCompare { 172 public: operator()173 bool operator () (const Macro& lhs, const Macro& rhs) const 174 { 175 size_t lhash = lhs.idHash(); 176 size_t rhash = rhs.idHash(); 177 if (lhash < rhash) return true; 178 else if (lhash > rhash) return false; 179 180 int df = lhs.m_name.compare(rhs.m_name); 181 return df < 0; 182 } 183 }; 184 185 class NameArgHash { 186 public: operator()187 size_t operator () (const Macro& macro) const 188 { 189 return macro.idHash(); 190 } 191 }; 192 193 class NameArgEqual { 194 public: operator()195 bool operator () (const Macro& lhs, const Macro& rhs) const 196 { 197 int df = lhs.m_name.compare(rhs.m_name); 198 if (df == 0) { 199 if (lhs.m_hasArguments != rhs.m_hasArguments) { 200 return false; 201 } else { 202 if (lhs.m_argumentList.count() != rhs.m_argumentList.count()) return false; 203 /*QStringList::const_iterator it2 = rhs.m_argumentList.begin(); 204 for(QStringList::const_iterator it = lhs.m_argumentList.begin(); it != lhs.m_argumentList.end();) { 205 if(*it != *it2) return false; 206 207 ++it; 208 ++it2; 209 }*/ 210 return true; 211 212 } 213 } 214 return false; 215 } 216 }; 217 Macro(const Macro & source)218 Macro(const Macro& source) 219 : m_idHashValid(source.m_idHashValid), 220 m_valueHashValid(source.m_valueHashValid), 221 m_idHash(source.m_idHash), 222 m_valueHash(source.m_valueHash), 223 m_name(source.m_name), 224 m_fileName(source.m_fileName), 225 m_line(source.m_line), 226 m_column(source.m_column), 227 m_body(source.m_body), 228 m_hasArguments(source.m_hasArguments), 229 m_argumentList(source.m_argumentList), 230 m_isUndefMacro(source.m_isUndefMacro) 231 {} 232 233 Macro& operator = (const Macro& source) 234 { 235 m_idHashValid = source.m_idHashValid; 236 m_valueHashValid = source.m_valueHashValid; 237 m_idHash = source.m_idHash; 238 m_valueHash = source.m_valueHash; 239 m_name = source.m_name; 240 m_fileName = source.m_fileName; 241 m_line = source.m_line; 242 m_column = source.m_column; 243 m_body = source.m_body; 244 m_hasArguments = source.m_hasArguments; 245 m_argumentList = source.m_argumentList; 246 m_isUndefMacro = source.m_isUndefMacro; 247 return *this; 248 } 249 250 bool operator == (const Macro& source) const 251 { 252 if (!m_idHashValid || !m_valueHashValid) computeHash(); 253 if (!source.m_idHashValid || !source.m_valueHashValid) source.computeHash(); 254 255 if (m_idHash != source.m_idHash) return false; 256 if (m_valueHash != source.m_valueHash) return false; 257 258 return m_name == source.m_name && 259 m_fileName == source.m_fileName && 260 m_body == source.m_body && 261 m_hasArguments == source.m_hasArguments && 262 m_argumentList == source.m_argumentList && m_isUndefMacro == source.m_isUndefMacro; 263 } 264 read(QDataStream & stream)265 void read(QDataStream& stream) 266 { 267 Q_INT8 i; 268 stream >> i; 269 m_idHashValid = i; 270 stream >> i; 271 m_valueHashValid = i; 272 stream >> i; 273 m_hasArguments = i; 274 275 stream >> m_idHash; 276 stream >> m_valueHash; 277 stream >> m_name; 278 stream >> m_line; 279 stream >> m_column; 280 stream >> m_body; 281 stream >> m_fileName; 282 stream >> m_argumentList; 283 } 284 write(QDataStream & stream)285 void write(QDataStream& stream) const 286 { 287 Q_INT8 i; 288 i = m_idHashValid; 289 stream << i; 290 i = m_valueHashValid; 291 stream << i; 292 i = m_hasArguments; 293 stream << i; 294 295 stream << m_idHash; 296 stream << m_valueHash; 297 stream << m_name; 298 stream << m_line; 299 stream << m_column; 300 stream << m_body; 301 stream << m_fileName; 302 stream << m_argumentList; 303 } 304 305 /** Get the name for this macro */ name()306 QString name() const 307 { 308 return m_name; 309 } 310 /** Set the name for this macro */ setName(const QString & name)311 void setName(const QString& name) 312 { 313 m_name = name; 314 invalidateHash(); 315 } 316 317 /** Get the file name that contains this macro */ fileName()318 QString fileName() const 319 { 320 return m_fileName; 321 } 322 /** Set the file name that contains this macro */ setFileName(const QString & fileName)323 void setFileName(const QString& fileName) 324 { 325 m_fileName = fileName; 326 invalidateHash(); 327 } 328 329 /** Get the line the macro is defined on */ line()330 int line() const 331 { 332 return m_line; 333 } 334 /** Set the line the macro is defined on */ setLine(int line)335 void setLine(int line) 336 { 337 m_line = line; 338 } 339 340 /** Get the column the macro starts at */ column()341 int column() const 342 { 343 return m_column; 344 } 345 /** Set the column the macro starts at */ setColumn(int column)346 void setColumn(int column) 347 { 348 m_column = column; 349 } 350 351 /** Get the body of the macro */ body()352 QString body() const 353 { 354 return m_body; 355 } 356 /** Set the body of the macro */ setBody(const QString & body)357 void setBody(const QString& body) 358 { 359 m_body = body; 360 invalidateHash(); 361 } 362 363 /** This is used so the lexer does not have to remove macros that should really stay(they are just temporarily shadowed by an isUndef-macro */ isUndef()364 bool isUndef() const 365 { 366 return m_isUndefMacro; 367 }; 368 setUndef()369 void setUndef() 370 { 371 m_isUndefMacro = true; 372 invalidateHash(); 373 }; 374 375 /** Check whether the macro has arguments that are passed to it */ hasArguments()376 bool hasArguments() const 377 { 378 return m_hasArguments; 379 } setHasArguments(bool hasArguments)380 void setHasArguments(bool hasArguments) 381 { 382 m_hasArguments = hasArguments; 383 invalidateHash(); 384 } 385 /** Get a list of arguments passed to this macro */ argumentList()386 QList<Argument> argumentList() const 387 { 388 return m_argumentList; 389 } 390 391 /** Clear the list of arguments this macro has */ clearArgumentList()392 void clearArgumentList() 393 { 394 m_argumentList.clear(); 395 m_hasArguments = false; 396 invalidateHash(); 397 } 398 /** Add an argument to this macro */ addArgument(const Argument & argument)399 void addArgument(const Argument& argument) 400 { 401 m_argumentList << argument; 402 } 403 /** Add a list of arguments to this macro */ addArgumentList(const QList<Argument> & arguments)404 void addArgumentList(const QList<Argument>& arguments) 405 { 406 m_argumentList += arguments; 407 invalidateHash(); 408 } 409 410 ///This hash respects macro-name and argument-count idHash()411 size_t idHash() const 412 { 413 if (!m_idHashValid) computeHash(); 414 return m_idHash; 415 } 416 417 ///This hash respects body and if it is an undef-macro valueHash()418 size_t valueHash() const 419 { 420 if (!m_valueHashValid) computeHash(); 421 return m_valueHash; 422 } 423 424 private: invalidateHash()425 inline void invalidateHash() const 426 { 427 m_idHashValid = m_valueHashValid = false; 428 } 429 computeHash()430 void computeHash() const 431 { 432 m_idHash = 7 * (HashedString::hashString(m_name)); 433 int a = 1; 434 //m_idHash += 31 * m_argumentList.count(); 435 436 m_valueHash = 27 * (HashedString::hashString(m_body) + (m_isUndefMacro ? 1 : 0)); 437 438 for (QList<Argument>::const_iterator it = m_argumentList.begin(); it != m_argumentList.end(); ++it) { 439 a *= 19; 440 m_valueHash += a * HashedString::hashString(*it); 441 } 442 m_valueHashValid = true; 443 m_idHashValid = true; 444 } 445 446 mutable bool m_idHashValid; 447 mutable bool m_valueHashValid; 448 mutable size_t m_idHash; //Hash that represents the ids of all macros 449 mutable size_t m_valueHash; //Hash that represents the values of all macros 450 451 QString m_name; 452 QString m_fileName; 453 int m_line; 454 int m_column; 455 QString m_body; 456 bool m_hasArguments; 457 QStringList m_argumentList; //While identification, only the count plays a role, not the values. 458 bool m_isUndefMacro; 459 friend class NameCompare; 460 friend class NameArgEqual; 461 }; 462 463 class MacroSet 464 { 465 public: 466 //typedef __gnu_cxx::hash_set< Macro, Macro::NameArgHash, Macro::NameArgEqual > Macros; 467 typedef std::set< Macro, Macro::NameCompare > Macros; MacroSet()468 MacroSet() 469 : m_idHashValid(false), 470 m_valueHashValid(false), 471 m_idHash(0), 472 m_valueHash(0) 473 { 474 } 475 476 void addMacro(const Macro& macro); 477 read(QDataStream & stream)478 void read(QDataStream& stream) 479 { 480 //stream >> m_idHashValid >> m_idHash >> m_valueHashValid >> m_valueHash; 481 m_idHashValid = false; 482 m_valueHashValid = false; 483 int cnt; 484 stream >> cnt; 485 m_usedMacros.clear(); 486 Macro m; 487 for (int a = 0; a < cnt; a++) { 488 m.read(stream); 489 m_usedMacros.insert(m); 490 } 491 } 492 write(QDataStream & stream)493 void write(QDataStream& stream) const 494 { 495 //stream << m_idHashValid << m_idHash << m_valueHashValid << m_valueHash; 496 stream << int(m_usedMacros.size()); 497 for (Macros::const_iterator it = m_usedMacros.begin(); it != m_usedMacros.end(); ++it) { 498 (*it).write(stream); 499 } 500 } 501 502 bool hasMacro(const QString& name) const; 503 bool hasMacro(const HashedString& name) const; 504 Macro macro(const QString& name) const; 505 506 size_t idHash() const; 507 size_t valueHash() const; 508 macros()509 const Macros& macros() const 510 { 511 return m_usedMacros; 512 } 513 514 void merge(const MacroSet& macros); 515 private: 516 void computeHash() const; 517 Macros m_usedMacros; 518 mutable bool m_idHashValid; 519 mutable bool m_valueHashValid; 520 mutable size_t m_idHash; //Hash that represents the ids of all macros 521 mutable size_t m_valueHash; //Hash that represents the values of all macros 522 523 friend class Driver; 524 }; 525 526 #endif 527