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
|