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