1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2020 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15 
16 #ifndef SCOPEDTYPEVARIANT_H
17 #define SCOPEDTYPEVARIANT_H
18 
19 #include <utility>
20 #include <vector>
21 
22 #include "qcstring.h"
23 #include "definition.h"
24 
25 //! Class representing a local class definition found while
26 //! generating syntax highlighted code.
27 class LocalDef
28 {
29   public:
insertBaseClass(const QCString & name)30     void insertBaseClass(const QCString &name) { m_baseClasses.push_back(name); }
baseClasses()31     std::vector<QCString> baseClasses() const { return m_baseClasses; }
32   private:
33     std::vector<QCString> m_baseClasses;
34 };
35 
36 //-----------------------------------------------------------------------------
37 
38 /*! Variant class for a scoped type.
39  *
40  *  Variants:
41  *  - Dummy: a type used for hiding a global type.
42  *  - Local: a locally defined type (e.g. found inside a function)
43  *  - Global: a globally defined type (processed by doxygen in an earlier pass).
44  */
45 class ScopedTypeVariant
46 {
47   public:
48     //! possible variant types
49     enum Variant
50     {
51       Global,
52       Local,
53       Dummy
54     };
55     //! default constructor for creating a variant of type Dummy
ScopedTypeVariant()56     ScopedTypeVariant() : m_variant(Dummy)
57     {
58       m_u.globalDef = 0;
59     }
60     //! constructor for creating a variant of type Global
ScopedTypeVariant(const Definition * d)61     explicit ScopedTypeVariant(const Definition *d)
62     {
63       if (d)
64       {
65         m_name = d->name();
66         m_variant = Global;
67         m_u.globalDef = d;
68       }
69       else
70       {
71         m_variant = Dummy;
72         m_u.globalDef = 0;
73       }
74     }
75     //! constructor for creating a variant of type Local
ScopedTypeVariant(const QCString & name)76     explicit ScopedTypeVariant(const QCString &name)
77     {
78       m_name = name;
79       m_variant = Local;
80       m_u.localDef = new LocalDef;
81     }
82     //! copy constructor
ScopedTypeVariant(const ScopedTypeVariant & stv)83     ScopedTypeVariant(const ScopedTypeVariant &stv)
84     {
85       m_variant = stv.m_variant;
86       m_name    = stv.m_name;
87       if (m_variant==Local)
88       {
89         m_u.localDef = new LocalDef(*stv.m_u.localDef);
90       }
91       else if (m_variant==Global)
92       {
93         m_u.globalDef = stv.m_u.globalDef;
94       }
95     }
96     //! move constructor
ScopedTypeVariant(ScopedTypeVariant && stv)97     ScopedTypeVariant(ScopedTypeVariant &&stv) noexcept : ScopedTypeVariant()
98     {
99       swap(*this,stv);
100     }
101     //! assignment operator
102     ScopedTypeVariant &operator=(ScopedTypeVariant stv)
103     {
104       swap(*this,stv);
105       return *this;
106     }
107     //! destructor
~ScopedTypeVariant()108    ~ScopedTypeVariant()
109     {
110       if (m_variant==Local)
111       {
112         delete m_u.localDef;
113       }
114     }
115     //! swap function
swap(ScopedTypeVariant & first,ScopedTypeVariant & second)116     friend void swap(ScopedTypeVariant &first,ScopedTypeVariant &second)
117     {
118       using std::swap; // enable ADL
119       swap(first.m_variant,second.m_variant);
120       swap(first.m_name,second.m_name);
121       swap(first.m_u.globalDef,second.m_u.globalDef);
122     }
123     //! Turn the variant into a Global type
setGlobal(const Definition * def)124     void setGlobal(const Definition *def)
125     {
126       if (m_variant==Local)
127       {
128         delete m_u.localDef;
129       }
130       m_variant = Global;
131       m_name = def->name();
132       m_u.globalDef = def;
133     }
134     //! Turn the variant into a Local type
setLocal(const QCString & name)135     LocalDef *setLocal(const QCString &name)
136     {
137       if (m_variant==Local)
138       {
139         delete m_u.localDef;
140       }
141       m_variant = Local;
142       m_name = name;
143       m_u.localDef = new LocalDef;
144       return m_u.localDef;
145     }
146     //! Turn the variant into a Dummy type
setDummy()147     void setDummy()
148     {
149       if (m_variant==Local)
150       {
151         delete m_u.localDef;
152       }
153       m_variant = Dummy;
154       m_name = "";
155       m_u.localDef=0;
156     }
type()157     Variant type() const { return m_variant; }
name()158     QCString name() const { return m_name; }
localDef()159     LocalDef *localDef() const { return m_variant==Local ? m_u.localDef : 0; }
globalDef()160     const Definition *globalDef() const { return m_variant==Global ? m_u.globalDef : 0; }
161 
162   private:
163     Variant m_variant;
164     QCString m_name;
165     union
166     {
167       const Definition *globalDef;
168       LocalDef *localDef;
169     } m_u;
170 };
171 
172 //-----------------------------------------------------------------------------
173 
174 /*! Represents a stack of variable to class mappings as found in the
175  *  code. Each scope is enclosed in pushScope() and popScope() calls.
176  *  Variables are added by calling addVariables() and one can search
177  *  for variable using findVariable().
178  */
179 class VariableContext
180 {
181   public:
182     using Scope = std::unordered_map<std::string,ScopedTypeVariant>;
183 
pushScope()184     void pushScope()
185     {
186       m_scopes.push_back(Scope());
187     }
popScope()188     void popScope()
189     {
190       if (!m_scopes.empty())
191       {
192         m_scopes.pop_back();
193       }
194     }
clear()195     void clear()
196     {
197       m_scopes.clear();
198       m_globalScope.clear();
199     }
clearExceptGlobal()200     void clearExceptGlobal()
201     {
202       m_scopes.clear();
203     }
addVariable(const QCString & name,ScopedTypeVariant stv)204     void addVariable(const QCString &name,ScopedTypeVariant stv)
205     {
206       Scope *scope = m_scopes.empty() ? &m_globalScope : &m_scopes.back();
207       scope->emplace(std::make_pair(name.str(),std::move(stv))); // add it to a list
208     }
findVariable(const QCString & name)209     const ScopedTypeVariant *findVariable(const QCString &name)
210     {
211       const ScopedTypeVariant *result = 0;
212       if (name.isEmpty()) return result;
213 
214       // search from inner to outer scope
215       auto it = std::rbegin(m_scopes);
216       while (it != std::rend(m_scopes))
217       {
218         auto it2 = it->find(name.str());
219         if (it2 != std::end(*it))
220         {
221           result = &it2->second;
222           return result;
223         }
224         ++it;
225       }
226       // nothing found -> also try the global scope
227       auto it2 = m_globalScope.find(name.str());
228       if (it2 != m_globalScope.end())
229       {
230         result = &it2->second;
231       }
232       return result;
233     }
atGlobalScope()234     bool atGlobalScope() const { return m_scopes.empty(); }
235 
236   private:
237     Scope              m_globalScope;
238     std::vector<Scope> m_scopes;
239 };
240 
241 //-----------------------------------------------------------------------------
242 
243 /** Represents the call context */
244 class CallContext
245 {
246   public:
247     struct Ctx
248     {
CtxCtx249       Ctx(const QCString &name_,const QCString &type_) : name(name_), type(type_) {}
250       QCString name;
251       QCString type;
252       ScopedTypeVariant stv;
253     };
254 
CallContext()255     CallContext()
256     {
257       clear();
258     }
setScope(const ScopedTypeVariant & stv)259     void setScope(const ScopedTypeVariant &stv)
260     {
261       Ctx &ctx = m_stvList.back();
262       ctx.stv=std::move(stv);
263     }
pushScope(const QCString & name_,const QCString & type_)264     void pushScope(const QCString &name_,const QCString &type_)
265     {
266       m_stvList.push_back(Ctx(name_,type_));
267     }
popScope(QCString & name_,QCString & type_)268     void popScope(QCString &name_,QCString &type_)
269     {
270       if (m_stvList.size()>1)
271       {
272         const Ctx &ctx = m_stvList.back();
273         name_ = ctx.name;
274         type_ = ctx.type;
275         m_stvList.pop_back();
276       }
277     }
clear()278     void clear()
279     {
280       m_stvList.clear();
281       m_stvList.push_back(Ctx(QCString(),QCString()));
282     }
getScope()283     const ScopedTypeVariant getScope() const
284     {
285       return m_stvList.back().stv;
286     }
287 
288   private:
289     std::vector<Ctx> m_stvList;
290 };
291 
292 
293 #endif
294