File: environmentmanager.h

package info (click to toggle)
kdevelop 4%3A4.3.1-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 18,844 kB
  • sloc: cpp: 91,758; python: 1,095; lex: 422; ruby: 120; sh: 114; xml: 42; makefile: 38
file content (389 lines) | stat: -rw-r--r-- 15,068 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/***************************************************************************
   Copyright 2006 David Nolden <david.nolden.kdevelop@art-master.de>
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef ENVIRONMENTMANAGER_H
#define ENVIRONMENTMANAGER_H

#include <map>
#include <QtCore/qglobal.h>

#include <QtCore/QDateTime>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QMutex>

#include <kurl.h>
#include <ksharedptr.h>

#include <language/duchain/parsingenvironment.h>
#include <language/editor/modificationrevision.h>
#include <language/interfaces/iproblem.h>
#include <language/duchain/topducontext.h>

#include "cppduchainexport.h"
#include <language/util/setrepository.h>
#include "parser/rpp/macrorepository.h"

/** 
 * The environment-manager helps achieving right representation of the way c++ works:
 * When a file is processed by the preprocessor, the same file may create totally
 * different results depending on the defined macros. Think for example of header-
 * guards.
 *
 * Now when one file includes another, we want to know whether there already
 * is a readily parsed du-context for the file that WOULD BE CREATED if it was
 * preprocessed under the current environment of macros.
 *
 * The environment-manager is there to answer that question:
 * EnvironmentFile collects all information about the context a file was parsed in,
 * the macros used, the words contained in a file that can be influenced by macros,
 * and the defined macros.
 *
 * The environment-manager is able to match that information agains a current parsing-environment
 * to see whether preprocessing the file would yield the same result as an already stored
 * run.
 *
 * If the result would be different, the file will be re-preprocessed, parsed, and imported.
 * Else the set of defined macros is taken from the stored EnvironmentFile,
 * and the already available du-context will be imported. The result: correct behavior, perfectly working header-guards, no missing macros, intelligent reparsing of changed headers, ...
 *
 * There is only one assumption made that needs to be fulfilled to be correct:
 * - Headers should be included at the top of the file
 * */

/**
 * Simplified matching:
 * Consider this case:
----- File 3.h
#ifndef HAD_3
#define HAD_3
int i;
#endif

----- File 2.h
#include "3.h"
int independent;

----- File test_1.h
#include "2.h"

----- File test_2.h
#include "3.h"
#include "2.h"

----- end

2.h and 3.h both depend on the macro HAD_3. In test_2.h, 3.h is included before 3.h, and when 2.h is included in the next line,
it needs to be completeley re-parsed, because 2.h depends on the macro HAD_3, and that macro is different(it is set) then when it was parsed
in test_1.h.

In practice this is very problematic, because it leads to massive multiple parsing of everything, which costs a lot of resources.
To solve this, "Simplified matching" can be used(it is enabled in CppLanguageSupport).
With simplified matching enabled, every file is always represented by at least 2 parts:
One part for all the headers at the top(we will call it header-section), and one part for the real content below the headers.
From the header-section, we create a proxy-context, that only stores the environment-matching information, and that imports the next created content-context.
The content-context starts right behind the header-context, and its environment-matching information only represents the real content.
The content-context can then be re-used many times, as long as the environment matches, while many different proxy-contexts will be created that represent
different constellations of macro-dependences across included headers.

The downside of this approach:
- Many different includes may be added to the content-context, coming from different proxy-contexts. This is not 100% correct visibility-wise.
- This only works perfectly if the includes are at the top, within one block. If this is not the case, the behavior will partially become like without simplified matching.
The good things:
- The general code-completion should be exactly as good as without simplified matching.
* */



namespace rpp {
  class pp_macro;
  class pp_macro;
  class Environment;
}

class CppPreprocessEnvironment;

namespace Cpp {

struct MacroSetRepository : public Utils::BasicSetRepository {
  MacroSetRepository() : Utils::BasicSetRepository("macro sets") {
  }
  virtual void itemRemovedFromSets(uint index);
};

///@todo Make string-sets work correctly with IndexedString reference-counting
struct KDEVCPPDUCHAIN_EXPORT IndexedStringConversion {
  KDevelop::IndexedString toItem(uint index) const {
    return KDevelop::IndexedString::fromIndex(index);
  }
  uint toIndex(const KDevelop::IndexedString& str) const {
    return str.index();
  }
};

struct KDEVCPPDUCHAIN_EXPORT MacroIndexConversion {
  const rpp::pp_macro& toItem(uint index) const;
  uint toIndex(const rpp::pp_macro& _macro) const;
};

struct KDEVCPPDUCHAIN_EXPORT StaticStringSetRepository {
  static Utils::BasicSetRepository* repository();
  struct Locker : public QMutexLocker {
    Locker() : QMutexLocker(repository()->mutex()) {
    }
  };
};

struct KDEVCPPDUCHAIN_EXPORT StaticMacroSetRepository {
  static Utils::BasicSetRepository* repository();
  struct Locker : public QMutexLocker {
    Locker() : QMutexLocker(repository()->mutex()) {
    }
  };
};

typedef Utils::StorableSet<KDevelop::IndexedString, IndexedStringConversion, StaticStringSetRepository, true, StaticStringSetRepository::Locker> ReferenceCountedStringSet;
typedef Utils::StorableSet<rpp::pp_macro, MacroIndexConversion, StaticMacroSetRepository, true, StaticMacroSetRepository::Locker> ReferenceCountedMacroSet;

class EnvironmentManager;
class MacroSet;

DECLARE_LIST_MEMBER_HASH(EnvironmentFileData, m_includePaths, KDevelop::IndexedString)

class EnvironmentFileData : public KDevelop::ParsingEnvironmentFileData {
public:

    EnvironmentFileData() {
      m_contentStartLine = 0;
//       m_includeFiles = 0;
      m_identityOffset = 0;
      m_includePaths = 0;
    }
    EnvironmentFileData(const EnvironmentFileData& rhs) : KDevelop::ParsingEnvironmentFileData(rhs) {
      m_url = rhs.m_url;
      m_strings = rhs.m_strings; //String-set
//       m_includeFiles = rhs.m_includeFiles; //String-set
      m_missingIncludeFiles = rhs.m_missingIncludeFiles; //String-set
      m_usedMacros = rhs.m_usedMacros; //Macro-set
      m_usedMacroNames = rhs.m_usedMacroNames; //String-set
      m_definedMacros = rhs.m_definedMacros; //Macro-set
      m_definedMacroNames = rhs.m_definedMacroNames; //String-set
      m_unDefinedMacroNames = rhs.m_unDefinedMacroNames; //String-set
      m_contentStartLine = rhs.m_contentStartLine;
      m_topContext = rhs.m_topContext;
      m_identityOffset = rhs.m_identityOffset;
      m_includePaths = rhs.m_includePaths;
      m_guard = rhs.m_guard;
      m_includePathDependencies = rhs.m_includePathDependencies;
    }
    
    ~EnvironmentFileData() {
    }
    uint m_identityOffset;
    //All the following sets get their reference-count increased whenever put in here
    //Set of all strings that can be affected by macros from outside
    ReferenceCountedStringSet m_strings;
    ReferenceCountedStringSet m_missingIncludeFiles;
    ReferenceCountedMacroSet m_usedMacros;
    ReferenceCountedStringSet m_usedMacroNames;
    ReferenceCountedMacroSet m_definedMacros;
    ReferenceCountedStringSet m_definedMacroNames;
    ReferenceCountedStringSet m_unDefinedMacroNames;
    
    uint m_includePaths; //Index in the internal include-paths repository
    int m_contentStartLine;
    
    //Name of the header-guard macro that protects this file
    KDevelop::IndexedString m_guard;
    
    KDevelop::ModificationRevisionSet m_includePathDependencies;
};

class KDEVCPPDUCHAIN_EXPORT EnvironmentFile : public KDevelop::ParsingEnvironmentFile {
  public:
    ///@todo Respect changing include-paths: Check if the included files are still the same(maybe new files are found that were not found before)
    EnvironmentFile( const KDevelop::IndexedString& url, KDevelop::TopDUContext* topContext );

    EnvironmentFile( EnvironmentFileData& data );
    
    ~EnvironmentFile();
    
    void addStrings( const std::set<Utils::BasicSetRepository::Index>& strings );

    ///If there previously was a macro defined of the same name, it must be given through previousOfSameName, else it can be zero.
    void addDefinedMacro( const rpp::pp_macro& macro, const rpp::pp_macro* previousOfSameName );

    ///the given macro will only make it into usedMacros() if it was not defined in this file
    void usingMacro( const rpp::pp_macro& macro );

    void addIncludeFile( const KDevelop::IndexedString& file, const KDevelop::ModificationRevision& modificationTime );

    ///Returns the set of all strings that can affect this file from outside.
    const ReferenceCountedStringSet& strings() const;
    
    ///The parameter should be a EnvironmentFile that was processed AFTER the content of this file
    ///@param file The file to merge
    ///@param env Optionally, the current environment that is active _before_ the
    ///           included file is "virtually" processed.
    ///@warning The file must _not_ be merged yet into the environment when this is called. Also,
    ///         after merging the environment-files a file cannot be merged into the environment any more,
    ///         so this should only be used indirectly through CppPreprocessEnvironment::merge
    void merge( const EnvironmentFile& file, CppPreprocessEnvironment* env = 0 );

    bool operator <  ( const EnvironmentFile& rhs ) const {
      return url() < rhs.url();
    }

    size_t hash() const;

    /**Set of all files with absolute paths, including those included indirectly
     *
     * This by definition also includes this file, so when the count is 1,
     * no other files were included.
     *
     * */
    //const IndexedStringSet& includeFiles() const;

    void addMissingIncludeFile(const KDevelop::IndexedString& file);
    const ReferenceCountedStringSet& missingIncludeFiles() const;

    void clearMissingIncludeFiles();
  
    KDevelop::IndexedString headerGuard() const;
    void setHeaderGuard( KDevelop::IndexedString guardName );
    
    ///Set of all defined macros, including those of all deeper included files
    const ReferenceCountedMacroSet& definedMacros() const;

    ///Set of all macros used from outside, including those used in deeper included files
    const ReferenceCountedMacroSet& usedMacros() const;

    const ReferenceCountedStringSet& usedMacroNames() const;
    
    const ReferenceCountedStringSet& definedMacroNames() const;
    
    ///Set of all macros undefined to the outside
    const ReferenceCountedStringSet& unDefinedMacroNames() const;
  
    ///Should return the include-paths that were used while parsing this file(as used/found in CppLanguageSupport)
    const QList<KDevelop::IndexedString> includePaths() const;
    void setIncludePaths( const QList<KDevelop::IndexedString>& paths );

    ///Set the first line of actual content, behind includes etc.
    void setContentStartLine(int line);
    int contentStartLine() const;

    /**
    * The identity-value usually only depends on the content of the environment-information. This can be used to separate environments that have the same content.
    * For example a content-environment from a proxy-environment.
    * */
    void setIdentityOffset(uint offset);
    uint identityOffset() const;
    
    virtual bool matchEnvironment(const KDevelop::ParsingEnvironment* environment) const;
    
    virtual bool needsUpdate(const KDevelop::ParsingEnvironment* environment = 0) const;
    
    const KDevelop::ModificationRevisionSet& includePathDependencies() const;
    void  setIncludePathDependencies(const KDevelop::ModificationRevisionSet&);
    
    enum {
      Identity = 73
    };
    
  private:
    
    virtual int type() const;

    friend class EnvironmentManager;

    DUCHAIN_DECLARE_DATA(EnvironmentFile)
    /*
    Needed data:
    1. Set of all strings that appear in this file(For memory-reasons they should be taken from a global string-repository, because many will be the same)
    2. Set of all macros that were defined outside of, but affected the file

    Algorithm:
      Iterate over all available macros, and check whether they affect the file. If it does, make sure that the macro is in the macro-set and has the same body.
      If the check fails: We need to reparse.
    */
};

typedef KSharedPtr<EnvironmentFile>  EnvironmentFilePointer;

class KDEVCPPDUCHAIN_EXPORT EnvironmentManager {
  public:
    static EnvironmentManager* self()
    {
      Q_ASSERT_X(m_self, "EnvironmentManager::self()", "call EnvironmentManager::init() before ::self()");
      return m_self;
    }
    /**
     * Initialize the static EnvironmentManager
     */
    static void init();

    MacroDataRepository& macroDataRepository()
    {
      return m_macroDataRepository;
    }
    //Set-repository that contains the string-sets
    Utils::StringSetRepository& stringSetRepository()
    {
      return m_stringSetRepository;
    }
    //Set-repository that contains the macro-sets
    MacroSetRepository& macroSetRepository()
    {
      return m_macroSetRepository;
    }

    ///See the comment about simplified matching at the top
    void setSimplifiedMatching(bool simplified);
    bool isSimplifiedMatching() const {
      return m_simplifiedMatching;
    }
    
    enum MatchingLevel {
      IgnoreGuardsForImporting = 1,
      
      Disabled = IgnoreGuardsForImporting | (1<<5),
      Naive = IgnoreGuardsForImporting | (1<<6),
      Full = 1 << 7
    };
    
    bool ignoreGuardsForImporting() const {
      return matchingLevel() & IgnoreGuardsForImporting;
    }
    
    void setMatchingLevel(MatchingLevel level);
    MatchingLevel matchingLevel() const {
      return m_matchingLevel;
    }

  private:
    EnvironmentManager();
    static EnvironmentManager* m_self;
    MatchingLevel m_matchingLevel;
    bool m_simplifiedMatching;
    //Repository that contains the actual macros, and maps them to indices
    MacroDataRepository m_macroDataRepository;
    //Set-repository that contains the string-sets
    Utils::StringSetRepository m_stringSetRepository;
    //Set-repository that contains the macro-sets
    MacroSetRepository m_macroSetRepository;
};

}

#endif