diff --git a/src/libs/utils/synchronousprocess.cpp b/src/libs/utils/synchronousprocess.cpp index 667416a3406..597be2932be 100644 --- a/src/libs/utils/synchronousprocess.cpp +++ b/src/libs/utils/synchronousprocess.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include @@ -366,5 +368,108 @@ void SynchronousProcess::processStdErr(bool emitSignals) } } +// Path utilities + +enum OS_Type { OS_Mac, OS_Windows, OS_Unix }; + +#ifdef Q_OS_WIN +static const OS_Type pathOS = OS_Windows; +#else +# ifdef Q_OS_MAC +static const OS_Type pathOS = OS_Mac; +# else +static const OS_Type pathOS = OS_Unix; +# endif +#endif + +// Locate a binary in a directory, applying all kinds of +// extensions the operating system supports. +static QString checkBinary(const QDir &dir, const QString &binary) +{ + // naive UNIX approach + const QFileInfo info(dir.filePath(binary)); + if (info.isFile() && info.isExecutable()) + return info.absoluteFilePath(); + + // Does the OS have some weird extension concept or does the + // binary have a 3 letter extension? + if (pathOS == OS_Unix) + return QString(); + const int dotIndex = binary.lastIndexOf(QLatin1Char('.')); + if (dotIndex != -1 && dotIndex == binary.size() - 4) + return QString(); + + switch (pathOS) { + case OS_Unix: + break; + case OS_Windows: { + static const char *windowsExtensions[] = {".cmd", ".bat", ".exe", ".com" }; + // Check the Windows extensions using the order + const int windowsExtensionCount = sizeof(windowsExtensions)/sizeof(const char*); + for (int e = 0; e < windowsExtensionCount; e ++) { + const QFileInfo windowsBinary(dir.filePath(binary + QLatin1String(windowsExtensions[e]))); + if (windowsBinary.isFile() && windowsBinary.isExecutable()) + return windowsBinary.absoluteFilePath(); + } + } + break; + case OS_Mac: { + // Check for Mac app folders + const QFileInfo appFolder(dir.filePath(binary + QLatin1String(".app"))); + if (appFolder.isDir()) { + QString macBinaryPath = appFolder.absoluteFilePath(); + macBinaryPath += QLatin1String("/Contents/MacOS/"); + macBinaryPath += binary; + const QFileInfo macBinary(macBinaryPath); + if (macBinary.isFile() && macBinary.isExecutable()) + return macBinary.absoluteFilePath(); + } + } + break; + } + return QString(); +} + +QString SynchronousProcess::locateBinary(const QString &path, const QString &binary) +{ + // Absolute file? + const QFileInfo absInfo(binary); + if (absInfo.isAbsolute()) + return checkBinary(absInfo.dir(), absInfo.fileName()); + + // Windows finds binaries in the current directory + if (pathOS == OS_Windows) { + const QString currentDirBinary = checkBinary(QDir::current(), binary); + if (!currentDirBinary.isEmpty()) + return currentDirBinary; + } + + const QStringList paths = path.split(pathSeparator()); + if (paths.empty()) + return QString(); + const QStringList::const_iterator cend = paths.constEnd(); + for (QStringList::const_iterator it = paths.constBegin(); it != cend; ++it) { + const QDir dir(*it); + const QString rc = checkBinary(dir, binary); + if (!rc.isEmpty()) + return rc; + } + return QString(); +} + +QString SynchronousProcess::locateBinary(const QString &binary) +{ + const QByteArray path = qgetenv("PATH"); + return locateBinary(QString::fromLocal8Bit(path), binary); +} + +QChar SynchronousProcess::pathSeparator() +{ + if (pathOS == OS_Windows) + return QLatin1Char(';'); + return QLatin1Char(':'); +} + + } // namespace Utils } // namespace Core diff --git a/src/libs/utils/synchronousprocess.h b/src/libs/utils/synchronousprocess.h index e9218f43d80..80a04a1303e 100644 --- a/src/libs/utils/synchronousprocess.h +++ b/src/libs/utils/synchronousprocess.h @@ -114,6 +114,12 @@ public: SynchronousProcessResponse run(const QString &binary, const QStringList &args); + // Helpers to find binaries. Do not use it for other path variables + // and file types. + static QString locateBinary(const QString &binary); + static QString locateBinary(const QString &path, const QString &binary); + static QChar pathSeparator(); + signals: void stdOut(const QByteArray &data, bool firstTime); void stdErr(const QByteArray &data, bool firstTime); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 87f06931200..a3ad3ba84ff 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -102,8 +102,10 @@ GitClient::GitClient(GitPlugin* plugin) m_plugin(plugin), m_core(Core::ICore::instance()) { - if (QSettings *s = m_core->settings()) + if (QSettings *s = m_core->settings()) { m_settings.fromSettings(s); + m_binaryPath = m_settings.gitBinaryPath(); + } } GitClient::~GitClient() @@ -478,7 +480,7 @@ GitCommand *GitClient::createCommand(const QString &workingDirectory, if (m_settings.adoptPath) environment.set(QLatin1String("PATH"), m_settings.path); - GitCommand* command = new GitCommand(workingDirectory, environment); + GitCommand* command = new GitCommand(m_binaryPath, workingDirectory, environment); if (outputToWindow) { if (!editor) { // assume that the commands output is the important thing connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(appendDataAndPopup(QByteArray))); @@ -527,10 +529,9 @@ bool GitClient::synchronousGit(const QString &workingDirectory, { if (Git::Constants::debug) qDebug() << "synchronousGit" << workingDirectory << arguments; - const QString binary = QLatin1String(Constants::GIT_BINARY); if (logCommandToWindow) - m_plugin->outputWindow()->append(formatCommand(binary, arguments)); + m_plugin->outputWindow()->append(formatCommand(m_binaryPath, arguments)); QProcess process; process.setWorkingDirectory(workingDirectory); @@ -540,7 +541,7 @@ bool GitClient::synchronousGit(const QString &workingDirectory, environment.set(QLatin1String("PATH"), m_settings.path); process.setEnvironment(environment.toStringList()); - process.start(binary, arguments); + process.start(m_binaryPath, arguments); if (!process.waitForFinished()) { if (errorText) *errorText = "Error: Git timed out"; @@ -1000,6 +1001,6 @@ void GitClient::setSettings(const GitSettings &s) m_settings = s; if (QSettings *s = m_core->settings()) m_settings.toSettings(s); + m_binaryPath = m_settings.gitBinaryPath(); } } - diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index c920ffc1b32..edd7e05c9e3 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -176,6 +176,7 @@ private: GitPlugin *m_plugin; Core::ICore *m_core; GitSettings m_settings; + QString m_binaryPath; }; diff --git a/src/plugins/git/gitcommand.cpp b/src/plugins/git/gitcommand.cpp index 8362926cecc..54bfcea2ab4 100644 --- a/src/plugins/git/gitcommand.cpp +++ b/src/plugins/git/gitcommand.cpp @@ -61,8 +61,10 @@ GitCommand::Job::Job(const QStringList &a, int t) : { } -GitCommand::GitCommand(const QString &workingDirectory, +GitCommand::GitCommand(const QString &binaryPath, + const QString &workingDirectory, ProjectExplorer::Environment &environment) : + m_binaryPath(binaryPath), m_workingDirectory(workingDirectory), m_environment(environmentToList(environment)) { @@ -109,7 +111,7 @@ void GitCommand::run() if (Git::Constants::debug) qDebug() << "GitCommand::run" << j << '/' << count << m_jobs.at(j).arguments; - process.start(QLatin1String(Constants::GIT_BINARY), m_jobs.at(j).arguments); + process.start(m_binaryPath, m_jobs.at(j).arguments); if (!process.waitForFinished(m_jobs.at(j).timeout * 1000)) { ok = false; error += QLatin1String("Error: Git timed out"); diff --git a/src/plugins/git/gitcommand.h b/src/plugins/git/gitcommand.h index 32b76bf3485..1d661706151 100644 --- a/src/plugins/git/gitcommand.h +++ b/src/plugins/git/gitcommand.h @@ -43,9 +43,11 @@ namespace Internal { class GitCommand : public QObject { + Q_DISABLE_COPY(GitCommand) Q_OBJECT public: - explicit GitCommand(const QString &workingDirectory, + explicit GitCommand(const QString &binaryPath, + const QString &workingDirectory, ProjectExplorer::Environment &environment); @@ -67,8 +69,7 @@ private: int timeout; }; - QStringList environment() const; - + const QString m_binaryPath; const QString m_workingDirectory; const QStringList m_environment; diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp index 02a1acc1d9e..c379bb3fba5 100644 --- a/src/plugins/git/gitsettings.cpp +++ b/src/plugins/git/gitsettings.cpp @@ -32,9 +32,13 @@ ***************************************************************************/ #include "gitsettings.h" +#include "gitconstants.h" + +#include #include #include +#include static const char *groupC = "Git"; static const char *sysEnvKeyC = "SysEnv"; @@ -79,5 +83,30 @@ bool GitSettings::equals(const GitSettings &s) const return adoptPath == s.adoptPath && path == s.path && logCount == s.logCount && timeout == s.timeout; } +QString GitSettings::gitBinaryPath(bool *ok, QString *errorMessage) const +{ + // Locate binary in path if one is specified, otherwise default + // to pathless binary + if (ok) + *ok = true; + if (errorMessage) + errorMessage->clear(); + const QString binary = QLatin1String(Constants::GIT_BINARY); + // Easy, git is assumed to be elsewhere accessible + if (!adoptPath) + return binary; + // Search in path? + const QString pathBinary = Core::Utils::SynchronousProcess::locateBinary(path, binary); + if (pathBinary.isEmpty()) { + if (ok) + *ok = false; + if (errorMessage) + *errorMessage = QCoreApplication::translate("GitSettings", + "The binary '%1' could not be located in the path '%2'").arg(binary, path); + return binary; + } + return pathBinary; +} + } } diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h index c2463eb326d..05dc250c578 100644 --- a/src/plugins/git/gitsettings.h +++ b/src/plugins/git/gitsettings.h @@ -51,6 +51,8 @@ struct GitSettings void fromSettings(QSettings *); void toSettings(QSettings *) const; + QString gitBinaryPath(bool *ok = 0, QString *errorMessage = 0) const; + bool equals(const GitSettings &s) const; bool adoptPath; diff --git a/src/plugins/git/settingspage.cpp b/src/plugins/git/settingspage.cpp index 121c7cd889b..4fdd672eac4 100644 --- a/src/plugins/git/settingspage.cpp +++ b/src/plugins/git/settingspage.cpp @@ -36,8 +36,10 @@ #include "gitplugin.h" #include +#include -using namespace Git::Internal; +namespace Git { +namespace Internal { SettingsPageWidget::SettingsPageWidget(QWidget *parent) : QWidget(parent) @@ -101,6 +103,17 @@ void SettingsPage::apply() { if (!m_widget) return; + const GitSettings newSettings = m_widget->settings(); + // Warn if git cannot be found in path if the widget is on top + if (m_widget->isVisible()) { + bool gitFoundOk; + QString errorMessage; + newSettings.gitBinaryPath(&gitFoundOk, &errorMessage); + if (!gitFoundOk) + QMessageBox::warning(m_widget, tr("Git Settings"), errorMessage); + } - GitPlugin::instance()->setSettings(m_widget->settings()); + GitPlugin::instance()->setSettings(newSettings); +} +} } diff --git a/src/shared/cplusplus/ASTVisitor.cpp b/src/shared/cplusplus/ASTVisitor.cpp index 7b9ca1ed7fa..d08d1c4d93d 100644 --- a/src/shared/cplusplus/ASTVisitor.cpp +++ b/src/shared/cplusplus/ASTVisitor.cpp @@ -77,12 +77,7 @@ int ASTVisitor::tokenKind(unsigned index) const { return translationUnit()->tokenKind(index); } const char *ASTVisitor::spell(unsigned index) const -{ - if (! index) - return 0; - - return translationUnit()->tokenAt(index).spell(); -} +{ return translationUnit()->spell(index); } Identifier *ASTVisitor::identifier(unsigned index) const { return translationUnit()->identifier(index); } diff --git a/src/shared/cplusplus/CheckDeclaration.cpp b/src/shared/cplusplus/CheckDeclaration.cpp index f8ddde8b48e..038583c27d4 100644 --- a/src/shared/cplusplus/CheckDeclaration.cpp +++ b/src/shared/cplusplus/CheckDeclaration.cpp @@ -389,6 +389,11 @@ bool CheckDeclaration::visit(UsingDirectiveAST *ast) UsingNamespaceDirective *u = control()->newUsingNamespaceDirective(ast->firstToken(), name); ast->symbol = u; _scope->enterSymbol(u); + + if (! (_scope->isBlockScope() || _scope->isNamespaceScope())) + translationUnit()->error(ast->firstToken(), + "using-directive not within namespace or block scope"); + return false; } diff --git a/src/shared/cplusplus/TranslationUnit.cpp b/src/shared/cplusplus/TranslationUnit.cpp index 40a95c0f05d..ebadf35c75a 100644 --- a/src/shared/cplusplus/TranslationUnit.cpp +++ b/src/shared/cplusplus/TranslationUnit.cpp @@ -134,6 +134,14 @@ const Token &TranslationUnit::tokenAt(unsigned index) const int TranslationUnit::tokenKind(unsigned index) const { return _tokens->at(index).kind; } +const char *TranslationUnit::spell(unsigned index) const +{ + if (! index) + return 0; + + return _tokens->at(index).spell(); +} + Identifier *TranslationUnit::identifier(unsigned index) const { return _tokens->at(index).identifier; } diff --git a/src/shared/cplusplus/TranslationUnit.h b/src/shared/cplusplus/TranslationUnit.h index aa490701ef9..7a57950ac34 100644 --- a/src/shared/cplusplus/TranslationUnit.h +++ b/src/shared/cplusplus/TranslationUnit.h @@ -87,6 +87,7 @@ public: unsigned tokenCount() const; const Token &tokenAt(unsigned index) const; int tokenKind(unsigned index) const; + const char *spell(unsigned index) const; unsigned matchingBrace(unsigned index) const; Identifier *identifier(unsigned index) const; diff --git a/tests/manual/binding/main.cpp b/tests/manual/binding/main.cpp index 8e889b6b514..5c3545339e1 100644 --- a/tests/manual/binding/main.cpp +++ b/tests/manual/binding/main.cpp @@ -43,6 +43,8 @@ #include #include +#include + #include #include #include @@ -55,6 +57,40 @@ // NamespaceBinding //////////////////////////////////////////////////////////////////////////////// +class Location +{ +public: + Location() + : _fileId(0), + _sourceLocation(0) + { } + + Location(Symbol *symbol) + : _fileId(symbol->fileId()), + _sourceLocation(symbol->sourceLocation()) + { } + + Location(StringLiteral *fileId, unsigned sourceLocation) + : _fileId(fileId), _sourceLocation(sourceLocation) + { } + + inline bool isValid() const + { return _fileId != 0; } + + inline operator bool() const + { return _fileId != 0; } + + inline StringLiteral *fileId() const + { return _fileId; } + + inline unsigned sourceLocation() const + { return _sourceLocation; } + +private: + StringLiteral *_fileId; + unsigned _sourceLocation; +}; + class NamespaceBinding { public: @@ -79,6 +115,10 @@ public: /// Returns the binding associated with the given symbol. NamespaceBinding *findOrCreateNamespaceBinding(Namespace *symbol); + NamespaceBinding *resolveNamespace(const Location &loc, + Name *name, + bool lookAtParent = true); + /// Helpers. std::string qualifiedId() const; void dump(); @@ -96,6 +136,9 @@ public: // attributes /// This binding's connections. Array children; + /// This binding's list of using namespaces. + Array usings; + /// This binding's namespace symbols. Array symbols; }; @@ -211,6 +254,104 @@ NamespaceBinding *NamespaceBinding::findOrCreateNamespaceBinding(Namespace *symb return binding; } +static void closure(const Location &loc, + NamespaceBinding *binding, Name *name, + Array *bindings) +{ + for (unsigned i = 0; i < bindings->size(); ++i) { + NamespaceBinding *b = bindings->at(i); + + if (b == binding) + return; + } + + bindings->push_back(binding); + + assert(name->isNameId()); + + Identifier *id = name->asNameId()->identifier(); + bool ignoreUsingDirectives = false; + + for (unsigned i = 0; i < binding->symbols.size(); ++i) { + Namespace *symbol = binding->symbols.at(i); + Scope *scope = symbol->members(); + + for (Symbol *symbol = scope->lookat(id); symbol; symbol = symbol->next()) { + if (symbol->name() != name || ! symbol->isNamespace()) + continue; + + const Location l(symbol); + + if (l.fileId() == loc.fileId() && l.sourceLocation() < loc.sourceLocation()) { + ignoreUsingDirectives = true; + break; + } + } + } + + if (ignoreUsingDirectives) + return; + + for (unsigned i = 0; i < binding->usings.size(); ++i) { + NamespaceBinding *u = binding->usings.at(i); + + closure(loc, u, name, bindings); + } +} + + +NamespaceBinding *NamespaceBinding::resolveNamespace(const Location &loc, + Name *name, + bool lookAtParent) +{ + if (! name) + return 0; + + else if (NameId *nameId = name->asNameId()) { + Array bindings; + closure(loc, this, nameId, &bindings); + + Array results; + + for (unsigned i = 0; i < bindings.size(); ++i) { + NamespaceBinding *binding = bindings.at(i); + + if (NamespaceBinding *b = binding->findNamespaceBinding(nameId)) + results.push_back(b); + } + + if (results.size() == 1) + return results.at(0); + + else if (results.size() > 1) { + // ### FIXME: return 0; + return results.at(0); + } + + else if (parent && lookAtParent) + return parent->resolveNamespace(loc, name); + + } else if (QualifiedNameId *q = name->asQualifiedNameId()) { + if (q->nameCount() == 1) { + assert(q->isGlobal()); + + return globalNamespaceBinding()->resolveNamespace(loc, q->nameAt(0)); + } + + NamespaceBinding *current = this; + if (q->isGlobal()) + current = globalNamespaceBinding(); + + current = current->resolveNamespace(loc, q->nameAt(0)); + for (unsigned i = 1; current && i < q->nameCount(); ++i) + current = current->resolveNamespace(loc, q->nameAt(i), false); + + return current; + } + + return 0; +} + // ### rewrite me std::string NamespaceBinding::qualifiedId() const { @@ -255,28 +396,38 @@ public: Binder(); virtual ~Binder(); - NamespaceBinding *operator()(Symbol *symbol) - { return bind(symbol, 0); } + NamespaceBinding *operator()(TranslationUnit *u, Namespace *globals) + { + TranslationUnit *previousUnit = unit; + unit = u; + NamespaceBinding *binding = bind(globals, 0); + unit = previousUnit; + return binding; + } protected: NamespaceBinding *bind(Symbol *symbol, NamespaceBinding *binding); NamespaceBinding *findOrCreateNamespaceBinding(Namespace *symbol); + NamespaceBinding *resolveNamespace(const Location &loc, Name *name); NamespaceBinding *switchNamespaceBinding(NamespaceBinding *binding); using SymbolVisitor::visit; virtual bool visit(Namespace *); + virtual bool visit(UsingNamespaceDirective *); virtual bool visit(Class *); virtual bool visit(Function *); virtual bool visit(Block *); private: NamespaceBinding *namespaceBinding; + TranslationUnit *unit; }; Binder::Binder() - : namespaceBinding(0) + : namespaceBinding(0), + unit(0) { } Binder::~Binder() @@ -299,6 +450,15 @@ NamespaceBinding *Binder::findOrCreateNamespaceBinding(Namespace *symbol) return namespaceBinding; } +NamespaceBinding *Binder::resolveNamespace(const Location &loc, Name *name) +{ + if (! namespaceBinding) + return 0; + + return namespaceBinding->resolveNamespace(loc, name); +} + + NamespaceBinding *Binder::switchNamespaceBinding(NamespaceBinding *binding) { NamespaceBinding *previousBinding = namespaceBinding; @@ -319,6 +479,20 @@ bool Binder::visit(Namespace *symbol) return false; } +bool Binder::visit(UsingNamespaceDirective *u) +{ + NamespaceBinding *resolved = resolveNamespace(Location(u), u->name()); + + if (! resolved) { + unit->error(u->sourceLocation(), "expected namespace-name"); + return false; + } + + namespaceBinding->usings.push_back(resolved); + + return false; +} + bool Binder::visit(Class *) { return false; } @@ -376,7 +550,7 @@ int main(int argc, char *argv[]) // bind Binder bind; - NamespaceBinding *binding = bind(globalNamespace); + NamespaceBinding *binding = bind(&unit, globalNamespace); binding->dump(); delete binding;