diff --git a/app_templates/CMakeLists.txt b/app_templates/CMakeLists.txt index 833446be96..fce401874b 100644 --- a/app_templates/CMakeLists.txt +++ b/app_templates/CMakeLists.txt @@ -1,6 +1,7 @@ kdevplatform_add_app_templates( cpp/CMake/cmake_qt4core cpp/CMake/cmake_qt4gui cpp/CMake/cmake_qt4core_qtest cpp/CMake/cmake_plaincpp + cpp/QMake/qmake_qt4guiapp ) diff --git a/app_templates/cpp/QMake/qmake_qt4guiapp/qmake_qt4guiapp.kdevtemplate b/app_templates/cpp/QMake/qmake_qt4guiapp/qmake_qt4guiapp.kdevtemplate index e7e983dcca..826c0a4bed 100644 --- a/app_templates/cpp/QMake/qmake_qt4guiapp/qmake_qt4guiapp.kdevtemplate +++ b/app_templates/cpp/QMake/qmake_qt4guiapp/qmake_qt4guiapp.kdevtemplate @@ -1,67 +1,67 @@ # KDE Config File [General] Name=Qt4 GUI Application Name[ca]=Una aplicació IGU per a les Qt4 Name[ca@valencia]=Una aplicació IGU per a les Qt4 Name[cs]=Aplikace prostředí Qt4 Name[de]=Qt-4-Anwendung mit grafischer Oberfläche Name[el]=Εφαρμογή GUI της Qt4 Name[en_GB]=Qt4 GUI Application Name[es]=Aplicación con interfaz gráfica Qt4 Name[et]=Qt4 GUI rakendus Name[fa]=کاربرد ونک Qt4 Name[fi]=Qt4 GUI -sovellus Name[fr]=Une application graphique avec Qt4 Name[ga]=Feidhmchlár Grafach Qt4 Name[gl]=Programa gráfico con Qt4 Name[hi]=क्यूटी४ जीयूआई अनुप्रयोग Name[hne]=क्यूटी४ जीयूआई अनुपरयोग Name[hu]=Qt4 GUI alkalmazás Name[lv]=Qt4 GUI programma Name[nds]=Qt4-Böversietprogramm Name[ne]=Qt4 GUI अनुप्रयोग Name[nl]=Qt4-GUI-toepassing Name[pl]=Aplikacja (GUI) QT4 Name[pt]=Uma Aplicação Gráfica do Qt4 Name[pt_BR]=Aplicativo gráfico do Qt4 Name[sk]=Aplikácia Qt4 GUI Name[sl]=Grafični program za Qt4 Name[sv]=Qt4-program med grafiskt användargränssnitt Name[tr]=Qt4 Arayüz Uygulaması Name[uk]=Програма Qt4 з графічним інтерфейсом Name[x-test]=xxQt4 GUI Applicationxx Name[zh_CN]=Qt4 图形界面应用程序 Name[zh_TW]=Qt4 使用者介面應用程式 -Category=C++/Qt (qmake) +Category=Qt/Graphical (QMake-based) Comment=Generate a QMake/Qt4 based application with graphical user interface (crossplatform compatible) Comment[ca]=Genera una aplicació basada en QMake/Qt4 amb interfície gràfica d'usuari (multi plataforma) Comment[ca@valencia]=Genera una aplicació basada en QMake/Qt4 amb interfície gràfica d'usuari (multi plataforma) Comment[de]=Erstellt eine auf QMake und Qt 4 basierende Anwendung mit grafischer Benutzeroberfläche (Plattformunabhängig) Comment[el]=Δημιουργία μιας εφαρμογής με βάση τα QMake/Qt4 με γραφικό περιβάλλον (συμβατή με πολλαπλές πλατφόρμες) Comment[en_GB]=Generate a QMake/Qt4 based application with graphical user interface (crossplatform compatible) Comment[es]=Genera una aplicación basada en QMake/Qt4 con interfaz gráfica de usuario (compatible multiplataforma) Comment[et]=Graafilise kasutajaliidesega QMake/Qt-põhise rakenduse loomine (multiplatvormne ühilduvus) Comment[fa]=یک کاربرد بر مبنای QMake/Qt4 با واسط کاربر نگاره‌ای تولید می‌کند )همساز بین سکویی( Comment[fi]=Generoi QMake/Qt4-perustainen sovellus graafisella käyttöliittymällä (toimii useissa käyttöjärjestelmissä) Comment[fr]=Générer une application graphique basée sur CMake / Qt4 (compatible multi-plate-forme) Comment[ga]=Cruthaigh feidhmchlár bunaithe ar QMake/Qt4 le comhéadan grafach (feidhmchlár trasardáin) Comment[gl]=Xerar un programa baseado en QMake e Qt 4 cunha interface gráfica de usuario, e compatibilidade con moitas plataformas. Comment[it]=Genera un'applicazione basata su QMake/Qt4 con interfaccia utente grafica (compatibile multipiattaforma) Comment[ja]=グラフィカルユーザインターフェースを持つ QMake/Qt4 ベースのアプリケーションを作成 (クロスプラットフォーム) Comment[lv]=Ģenerē QMake/Qt4 bāzētu programmu ar grafisku lietotāja saskarni (lietojamu uz daudzām platformām) Comment[nds]=En Programm mit graafsch Böversiet opstellen, dat op QMake un Qt4 opbuut (över Bedriefümgeven weg kompatibel) Comment[ne]=(क्रसप्लेटफर्मसँग मिल्दोजुल्दो) ग्राफिकल प्रयोगकर्ता इन्टरफेससँग QMake/Qt4 आधारित अनुप्रयोग सिर्जना गर्नुहोस् Comment[nl]=Genereert een op Qmake/Qt4 gebaseerde toepassing met grafische gebruikersinterface (crossplatform compatible) Comment[nn]=Lag eit QMake/Qt4-basert program med grafisk brukargrensesnitt (kryssplattform) Comment[pl]=Generuje program używający QMake i Qt/4 z graficznym interfejsem użytkownika (wieloplatformowy) Comment[pt]=Gera uma aplicação baseada no QMake/Qt4 com uma interface gráfica (compatível com várias plataformas) Comment[pt_BR]=Gera um aplicativo em QMake/Qt4 com uma interface gráfica (compatível com várias plataformas) Comment[sk]=Generovať aplikáciu QMake/Qt4 s grafickým používateľským rozhraním (crossplatformovo kompatibilný) Comment[sl]=Ustvari program temelječ na CMake/Qt4 z grafičnim uporabniškim vmesnikom (združljiv z več okolji) Comment[sv]=Skapa ett CMake/Qt4-baserat program med grafiskt användargränssnitt (som fungerar på flera plattformar) Comment[tr]=Grafik arayüz içeren bir QMake/Qt4 tabanlı bir uygulama oluştur (çapraz platform uyumlu) Comment[uk]=Створити програму, засновану на CMake/Qt4 (сумісну з декількома платформами) Comment[x-test]=xxGenerate a QMake/Qt4 based application with graphical user interface (crossplatform compatible)xx Comment[zh_CN]=生成基于 QMake/Qt4 的图形界面应用程序(跨平台兼容) Comment[zh_TW]=產生一個 QMake/Qt4 為基礎的使用者介面應用程式(跨平台相容) ShowFilesAfterGeneration=%{dest}/src/%{APPNAMELC}.cpp diff --git a/debuggers/gdb/CMakeLists.txt b/debuggers/gdb/CMakeLists.txt index 57eab84988..d2d88d9978 100644 --- a/debuggers/gdb/CMakeLists.txt +++ b/debuggers/gdb/CMakeLists.txt @@ -1,139 +1,131 @@ project(gdb) add_definitions(-DTRANSLATION_DOMAIN=\"kdevgdb\") -# TODO KF5 figure out how to get the process selection widget (ksysguardprocesslist.h) function(add_debuggable_executable target) cmake_parse_arguments(add_debuggable_executable "" "" "SRCS" ${ARGN}) add_executable(${target} ${add_debuggable_executable_SRCS}) # force debug symbols for our debuggees, disable optimizations if (WIN32) set(_flags "/0d") else() set(_flags "-O0") endif() set_target_properties(${target} PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} ${_flags}") endfunction() -if(NOT APPLE) -# macro_optional_find_package( KDE4Workspace 4.2.0 ) - -# macro_log_feature( KDE4WORKSPACE_FOUND -# "KDE 4 Workspace" "KDE 4 Workspace libraries" "kdebase/workspace" FALSE -# "" "Required for building the process selection dialog for attaching GDB to a running process." -# ) -endif(NOT APPLE) - -if(KDE4WORKSPACE_FOUND) - include_directories( - ${KDE4WORKSPACE_INCLUDE_DIR} - ) +find_package(KF5SysGuard) +if(KF5SysGuard_FOUND) add_definitions( -DKDEV_ENABLE_GDB_ATTACH_DIALOG ) endif() if (CMAKE_VERSION VERSION_GREATER "2.9" OR NOT CMAKE_GENERATOR MATCHES "Ninja") set(HAVE_PATH_WITH_SPACES_TEST TRUE) else() message(WARNING "Disabling 'path with spaces' test, this CMake version would create a faulty build.ninja file. Upgrade to at least CMake v3.0") endif() add_subdirectory(unittests) add_subdirectory(printers) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) #add_subdirectory(tests) ########### next target ############### set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}") set(kdevgdb_SRCS debuggerplugin.cpp gdb.cpp gdbcommandqueue.cpp gdbcommand.cpp stty.cpp disassemblewidget.cpp gdboutputwidget.cpp # debuggertracingdialog.cpp breakpointcontroller.cpp mi/gdbmi.cpp mi/milexer.cpp mi/miparser.cpp stringhelpers.cpp debugsession.cpp gdbconfigpage.cpp debugjob.cpp selectcoredialog.cpp variablecontroller.cpp gdbframestackmodel.cpp gdbvariable.cpp registers/registersview.cpp registers/registercontroller.cpp registers/registersmanager.cpp registers/registercontroller_x86.cpp registers/registercontroller_arm.cpp registers/modelsmanager.cpp registers/converters.cpp ) if(OKTETA_FOUND) add_definitions(-DWITH_OKTETA=1) list(APPEND kdevgdb_SRCS memviewdlg.cpp) endif() -if(KDE4WORKSPACE_FOUND) +if(KF5SysGuard_FOUND) set(kdevgdb_SRCS ${kdevgdb_SRCS} processselection.cpp ) -endif(KDE4WORKSPACE_FOUND) +endif(KF5SysGuard_FOUND) set(kdevgdb_UI debuggertracingdialog.ui gdbconfigpage.ui selectcoredialog.ui selectaddressdialog.ui registers/registersview.ui ) kde_enable_exceptions() ki18n_wrap_ui(kdevgdb_SRCS ${kdevgdb_UI}) qt5_add_resources(kdevgdb_SRCS kdevgdb.qrc) kdevplatform_add_plugin(kdevgdb JSON kdevgdb.json SOURCES ${kdevgdb_SRCS}) target_link_libraries(kdevgdb KDev::Sublime KDev::Interfaces KDev::Language KDev::Debugger KDev::OutputView KDev::Project KDev::Util KF5::TextEditor - ${KDE4WORKSPACE_PROCESSUI_LIBS} ) +if(KF5SysGuard_FOUND) + target_link_libraries(kdevgdb KF5::ProcessUi) +endif() set(test_gdb_SRCS unittests/test_gdb.cpp ${kdevgdb_SRCS}) ecm_add_test(${test_gdb_SRCS} TEST_NAME test_gdb LINK_LIBRARIES Qt5::Test KDev::Shell KDev::Interfaces KDev::Project KDev::Debugger KDev::Tests KDev::Util KF5::KIOWidgets KF5::TextEditor KF5::Parts - ${KDE4WORKSPACE_PROCESSUI_LIBS} ) +if(KF5SysGuard_FOUND) + target_link_libraries(test_gdb KF5::ProcessUi) +endif() if (HAVE_PATH_WITH_SPACES_TEST) set_target_properties(test_gdb PROPERTIES COMPILE_FLAGS "-DHAVE_PATH_WITH_SPACES_TEST") endif() diff --git a/debuggers/gdb/processselection.cpp b/debuggers/gdb/processselection.cpp index 8bee72e9d6..151b672c66 100644 --- a/debuggers/gdb/processselection.cpp +++ b/debuggers/gdb/processselection.cpp @@ -1,78 +1,97 @@ /* KDevelop GDB Support * * Copyright 2009 Aleix Pol * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "processselection.h" -#include "ksysguard/ksysguardprocesslist.h" -#include "ksysguard/process.h" +#include +#include +#include #include #include #include #include #include #include #include +#include +#include + namespace GDBDebugger { ProcessSelectionDialog::ProcessSelectionDialog(QWidget *parent) - : KDialog(parent) + : QDialog(parent) { - setCaption(i18n("Attach to a process")); + setWindowTitle(i18n("Attach to a process")); m_processList = new KSysGuardProcessList(this); - setMainWidget(m_processList); + QVBoxLayout* mainLayout = new QVBoxLayout; + setLayout(mainLayout); + mainLayout->addWidget(m_processList); + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok); + mainLayout->addWidget(buttonBox); + connect(m_processList->treeView()->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(selectionChanged())); m_processList->treeView()->setSelectionMode(QAbstractItemView::SingleSelection); m_processList->setState(ProcessFilter::UserProcesses); m_processList->setKillButtonVisible(false); m_processList->filterLineEdit()->setFocus(); //m_processList->setPidFilter(qApp->pid()); - button(Ok)->setEnabled(false); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + m_okButton = buttonBox->button(QDialogButtonBox::Ok); + m_okButton->setDefault(true); + m_okButton->setShortcut(Qt::CTRL | Qt::Key_Return); + m_okButton->setEnabled(false); KConfigGroup config = KSharedConfig::openConfig()->group("GdbProcessSelectionDialog"); m_processList->filterLineEdit()->setText(config.readEntry("filterText", QString())); m_processList->loadSettings(config); restoreGeometry(config.readEntry("dialogGeometry", QByteArray())); } ProcessSelectionDialog::~ProcessSelectionDialog() { KConfigGroup config = KSharedConfig::openConfig()->group("GdbProcessSelectionDialog"); config.writeEntry("filterText", m_processList->filterLineEdit()->text()); m_processList->saveSettings(config); config.writeEntry("dialogGeometry", saveGeometry()); } long int ProcessSelectionDialog::pidSelected() { QList ps=m_processList->selectedProcesses(); Q_ASSERT(ps.count()==1); KSysGuard::Process* process=ps.first(); - return process->pid; + return process->pid(); +} + +QSize ProcessSelectionDialog::sizeHint() const +{ + return QSize(740, 720); } void ProcessSelectionDialog::selectionChanged() { - button(Ok)->setEnabled(true); + m_okButton->setEnabled(true); } } diff --git a/debuggers/gdb/processselection.h b/debuggers/gdb/processselection.h index e6046550b6..6cc87ed7c4 100644 --- a/debuggers/gdb/processselection.h +++ b/debuggers/gdb/processselection.h @@ -1,47 +1,50 @@ /* KDevelop GDB Support * * Copyright 2009 Aleix Pol * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef PROCESSSELECTION_H #define PROCESSSELECTION_H -#include +#include class KSysGuardProcessList; +class QPushButton; namespace GDBDebugger { -class ProcessSelectionDialog : public KDialog +class ProcessSelectionDialog : public QDialog { Q_OBJECT public: ProcessSelectionDialog( QWidget *parent=0 ); ~ProcessSelectionDialog(); long int pidSelected(); + QSize sizeHint() const; private slots: void selectionChanged(); private: KSysGuardProcessList* m_processList; + QPushButton* m_okButton; }; } #endif diff --git a/languages/clang/clangparsejob.cpp b/languages/clang/clangparsejob.cpp index b408567f28..5f9b2af7ac 100644 --- a/languages/clang/clangparsejob.cpp +++ b/languages/clang/clangparsejob.cpp @@ -1,369 +1,369 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "clangparsejob.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clangsettings/clangsettingsmanager.h" #include "duchain/clanghelpers.h" #include "duchain/clangpch.h" #include "duchain/duchainutils.h" #include "duchain/parsesession.h" #include "duchain/clangindex.h" #include "duchain/clangparsingenvironmentfile.h" #include "util/clangdebug.h" #include "util/clangtypes.h" #include "clangsupport.h" #include "documentfinderhelpers.h" #include #include #include #include #include #include using namespace KDevelop; namespace { QString findConfigFile(const QString& forFile, const QString& configFileName) { QDir dir = QFileInfo(forFile).dir(); while (dir.exists()) { const QFileInfo customIncludePaths(dir, configFileName); if (customIncludePaths.exists()) { return customIncludePaths.absoluteFilePath(); } if (!dir.cdUp()) { break; } } return {}; } Path::List readPathListFile(const QString& filepath) { if (filepath.isEmpty()) { return {}; } QFile f(filepath); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { return {}; } const QString text = QString::fromLocal8Bit(f.readAll()); const QStringList lines = text.split(QLatin1Char('\n'), QString::SkipEmptyParts); Path::List paths(lines.length()); std::transform(lines.begin(), lines.end(), paths.begin(), [] (const QString& line) { return Path(line); }); return paths; } /** * File should contain the header to precompile and use while parsing * @returns the first path in the file */ Path userDefinedPchIncludeForFile(const QString& sourcefile) { static const QString pchIncludeFilename = QStringLiteral(".kdev_pch_include"); const auto paths = readPathListFile(findConfigFile(sourcefile, pchIncludeFilename)); return paths.isEmpty() ? Path() : paths.first(); } ProjectFileItem* findProjectFileItem(const IndexedString& url, bool* hasBuildSystemInfo) { ProjectFileItem* file = nullptr; *hasBuildSystemInfo = false; for (auto project: ICore::self()->projectController()->projects()) { auto files = project->filesForPath(url); if (files.isEmpty()) { continue; } file = files.last(); // A file might be defined in different targets. // Prefer file items defined inside a target with non-empty includes. for (auto f: files) { if (!dynamic_cast(f->parent())) { continue; } file = f; if (!IDefinesAndIncludesManager::manager()->includes(f, IDefinesAndIncludesManager::ProjectSpecific).isEmpty()) { break; } } } if (file && file->project()) { if (auto bsm = file->project()->buildSystemManager()) { *hasBuildSystemInfo = bsm->hasIncludesOrDefines(file); } } return file; } ClangParsingEnvironmentFile* parsingEnvironmentFile(const TopDUContext* context) { return dynamic_cast(context->parsingEnvironmentFile().data()); } DocumentChangeTracker* trackerForUrl(const IndexedString& url) { return ICore::self()->languageController()->backgroundParser()->trackerForUrl(url); } } ClangParseJob::ClangParseJob(const IndexedString& url, ILanguageSupport* languageSupport) : ParseJob(url, languageSupport) { const auto tuUrl = clang()->index()->translationUnitForUrl(url); bool hasBuildSystemInfo; if (auto file = findProjectFileItem(tuUrl, &hasBuildSystemInfo)) { m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includes(file)); m_environment.addDefines(IDefinesAndIncludesManager::manager()->defines(file)); m_environment.setParserSettings(ClangSettingsManager::self()->parserSettings(file)); } else { m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includes(tuUrl.str())); m_environment.addDefines(IDefinesAndIncludesManager::manager()->defines(tuUrl.str())); m_environment.setParserSettings(ClangSettingsManager::self()->parserSettings(nullptr)); } const bool isSource = ClangHelpers::isSource(tuUrl.str()); m_environment.setQuality( isSource ? (hasBuildSystemInfo ? ClangParsingEnvironment::BuildSystem : ClangParsingEnvironment::Source) : ClangParsingEnvironment::Unknown ); m_environment.setTranslationUnitUrl(tuUrl); Path::List projectPaths; const auto& projects = ICore::self()->projectController()->projects(); projectPaths.reserve(projects.size()); foreach (auto project, projects) { projectPaths.append(project->path()); } m_environment.setProjectPaths(projectPaths); foreach(auto document, ICore::self()->documentController()->openDocuments()) { auto textDocument = document->textDocument(); if (!textDocument || !textDocument->isModified() || !textDocument->url().isLocalFile() || !DocumentFinderHelpers::mimeTypesList().contains(textDocument->mimeType())) { continue; } m_unsavedFiles << UnsavedFile(textDocument->url().toLocalFile(), textDocument->textLines(textDocument->documentRange())); const IndexedString indexedUrl(textDocument->url()); m_unsavedRevisions.insert(indexedUrl, ModificationRevision::revisionForFile(indexedUrl)); } if (auto tracker = trackerForUrl(url)) { tracker->reset(); } } ClangSupport* ClangParseJob::clang() const { return static_cast(languageSupport()); } void ClangParseJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread */*thread*/) { QReadLocker parseLock(languageSupport()->parseLock()); if (abortRequested()) { return; } { const auto tuUrlStr = m_environment.translationUnitUrl().str(); m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includesInBackground(tuUrlStr)); m_environment.addDefines(IDefinesAndIncludesManager::manager()->definesInBackground(tuUrlStr)); m_environment.setPchInclude(userDefinedPchIncludeForFile(tuUrlStr)); } if (abortRequested()) { return; } // NOTE: we must have all declarations, contexts and uses available for files that are opened in the editor // it is very hard to check this for all included files of this TU, and previously lead to problems // when we tried to skip function bodies as an optimization for files that where not open in the editor. // now, we always build everything, which is correct but a tad bit slower. we can try to optimize later. setMinimumFeatures(static_cast(minimumFeatures() | TopDUContext::AllDeclarationsContextsAndUses)); if (minimumFeatures() & AttachASTWithoutUpdating) { // The context doesn't need to be updated, but has no AST attached (restored from disk), // so attach AST to it, without updating DUChain ParseSession session(createSessionData()); DUChainWriteLocker lock; auto ctx = DUChainUtils::standardContextForUrl(document().toUrl()); if (!ctx) { clangDebug() << "Lost context while attaching AST"; return; } ctx->setAst(IAstContainer::Ptr(session.data())); if (minimumFeatures() & UpdateHighlighting) { lock.unlock(); languageSupport()->codeHighlighting()->highlightDUChain(ctx); } return; } { UrlParseLock urlLock(document()); if (abortRequested() || !isUpdateRequired(ParseSession::languageString())) { return; } } ParseSession session(ClangIntegration::DUChainUtils::findParseSessionData(document(), m_environment.translationUnitUrl())); if (abortRequested()) { return; } if (!session.data() || !session.reparse(m_unsavedFiles, m_environment)) { session.setData(createSessionData()); } if (!session.unit()) { // failed to parse file, unpin and don't try again clang()->index()->unpinTranslationUnitForUrl(document()); return; } if (!clang_getFile(session.unit(), document().byteArray().constData())) { // this parse job's document does not exist in the pinned translation unit // so we need to unpin and re-add this document // Ideally we'd reset m_environment and session, but this is much simpler // and shouldn't be a common case clang()->index()->unpinTranslationUnitForUrl(document()); if (!(minimumFeatures() & Rescheduled)) { auto features = static_cast(minimumFeatures() | Rescheduled); ICore::self()->languageController()->backgroundParser()->addDocument(document(), features, priority()); } return; } Imports imports = ClangHelpers::tuImports(session.unit()); IncludeFileContexts includedFiles; if (auto pch = clang()->index()->pch(m_environment)) { auto pchFile = pch->mapFile(session.unit()); includedFiles = pch->mapIncludes(session.unit()); includedFiles.insert(pchFile, pch->context()); auto tuFile = clang_getFile(session.unit(), m_environment.translationUnitUrl().byteArray().constData()); imports.insert(tuFile, { pchFile, CursorInRevision(0, 0) } ); } if (abortRequested()) { return; } auto context = ClangHelpers::buildDUChain(session.mainFile(), imports, session, minimumFeatures(), includedFiles, clang()->index()); setDuChain(context); if (abortRequested()) { return; } - { + if (context) { if (minimumFeatures() & TopDUContext::AST) { DUChainWriteLocker lock; context->setAst(IAstContainer::Ptr(session.data())); } #ifdef QT_DEBUG DUChainReadLocker lock; auto file = parsingEnvironmentFile(context); Q_ASSERT(file); // verify that features and environment where properly set in ClangHelpers::buildDUChain Q_ASSERT(file->featuresSatisfied(TopDUContext::Features(minimumFeatures() & ~TopDUContext::ForceUpdateRecursive))); if (trackerForUrl(context->url())) { Q_ASSERT(file->featuresSatisfied(TopDUContext::AllDeclarationsContextsAndUses)); } #endif } foreach(const auto& context, includedFiles) { if (!context) { continue; } { // prefer the editor modification revision, instead of the on-disk revision auto it = m_unsavedRevisions.find(context->url()); if (it != m_unsavedRevisions.end()) { DUChainWriteLocker lock; auto file = parsingEnvironmentFile(context); Q_ASSERT(file); file->setModificationRevision(it.value()); } } if (trackerForUrl(context->url())) { if (clang()->index()->translationUnitForUrl(context->url()) == m_environment.translationUnitUrl()) { // cache the parse session and the contained translation unit for this chain // this then allows us to quickly reparse the document if it is changed by // the user // otherwise no editor component is open for this document and we can dispose // the TU to save memory // share the session data with all contexts that are pinned to this TU DUChainWriteLocker lock; context->setAst(IAstContainer::Ptr(session.data())); } languageSupport()->codeHighlighting()->highlightDUChain(context); } } } ParseSessionData::Ptr ClangParseJob::createSessionData() const { return ParseSessionData::Ptr(new ParseSessionData(m_unsavedFiles, clang()->index(), m_environment, ParseSessionData::NoOption)); } const ParsingEnvironment* ClangParseJob::environment() const { return &m_environment; } diff --git a/languages/clang/clangsupport.cpp b/languages/clang/clangsupport.cpp index bb02c5e969..56f2e3beb0 100644 --- a/languages/clang/clangsupport.cpp +++ b/languages/clang/clangsupport.cpp @@ -1,426 +1,451 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff Copyright 2014 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "clangsupport.h" #include "clangparsejob.h" #include "version.h" #include "util/clangdebug.h" #include "util/clangtypes.h" #include "codecompletion/model.h" #include "clanghighlighting.h" #include #include #include #include #include #include #include "codegen/adaptsignatureassistant.h" #include "duchain/documentfinderhelpers.h" #include "duchain/clangindex.h" #include "duchain/navigationwidget.h" #include "duchain/macrodefinition.h" #include "duchain/clangparsingenvironmentfile.h" #include "duchain/duchainutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clangsettings/sessionsettings/sessionsettings.h" #include #include +#include +#include + #include #include K_PLUGIN_FACTORY_WITH_JSON(KDevClangSupportFactory, "kdevclangsupport.json", registerPlugin(); ) using namespace KDevelop; namespace { /** * Extract the range of the path-spec inside the include-directive in line @p line * * Example: line = "#include " => returns {0, 10, 0, 16} * * @param originalRange This is the range that the resulting range will be based on * * @return Range pointing to the path-spec of the include or invalid range if there is no #include directive on the line. */ KTextEditor::Range rangeForIncludePathSpec(const QString& line, const KTextEditor::Range& originalRange = KTextEditor::Range()) { static const QRegularExpression pattern(QStringLiteral("^\\s*#include")); if (!line.contains(pattern)) { return KTextEditor::Range::invalid(); } KTextEditor::Range range = originalRange; int pos = 0; for (; pos < line.size(); ++pos) { if(line[pos] == QLatin1Char('"') || line[pos] == QLatin1Char('<')) { range.setStart({range.start().line(), ++pos}); break; } } for (; pos < line.size(); ++pos) { if(line[pos] == QLatin1Char('"') || line[pos] == QLatin1Char('>')) { range.setEnd({range.start().line(), pos}); break; } } if(range.start() > range.end()) { range.setStart(range.end()); } return range; } QPair lineInDocument(const QUrl &url, const KTextEditor::Cursor& position) { KDevelop::IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if (!doc || !doc->textDocument() || !ICore::self()->documentController()->activeTextDocumentView()) { return {}; } const int lineNumber = position.line(); const int lineLength = doc->textDocument()->lineLength(lineNumber); KTextEditor::Range range(lineNumber, 0, lineNumber, lineLength); QString line = doc->textDocument()->text(range); return {line, range}; } QPair importedContextForPosition(const QUrl &url, const KTextEditor::Cursor& position) { auto pair = lineInDocument(url, position); const QString line = pair.first; if (line.isEmpty()) return {{}, KTextEditor::Range::invalid()}; KTextEditor::Range wordRange = rangeForIncludePathSpec(line, pair.second); if (!wordRange.isValid()) { return {{}, KTextEditor::Range::invalid()}; } // Since this is called by the editor while editing, use a fast timeout so the editor stays responsive DUChainReadLocker lock(nullptr, 100); if (!lock.locked()) { clangDebug() << "Failed to lock the du-chain in time"; return {TopDUContextPointer(), KTextEditor::Range::invalid()}; } TopDUContext* topContext = DUChainUtils::standardContextForUrl(url); if (line.isEmpty() || !topContext || !topContext->parsingEnvironmentFile()) { return {TopDUContextPointer(), KTextEditor::Range::invalid()}; } // It's an #include, find out which file was included at the given line foreach(const DUContext::Import &imported, topContext->importedParentContexts()) { auto context = imported.context(nullptr); if (context) { if(topContext->transformFromLocalRevision(topContext->importPosition(context)).line() == wordRange.start().line()) { if (auto importedTop = dynamic_cast(context)) return {TopDUContextPointer(importedTop), wordRange}; } } } // The last resort. Check if the file is already included (maybe recursively from another files). // This is needed as clang doesn't visit (clang_getInclusions) those inclusions. // TODO: Maybe create an assistant that'll report whether the file is already included? auto includeName = line.mid(wordRange.start().column(), wordRange.end().column() - wordRange.start().column()); if (!includeName.isEmpty()) { if (includeName.startsWith(QLatin1Char('.'))) { const Path dir = Path(url).parent(); includeName = Path(dir, includeName).toLocalFile(); } const auto recursiveImports = topContext->recursiveImportIndices(); auto iterator = recursiveImports.iterator(); while (iterator) { const auto str = (*iterator).url().str(); if (str == includeName || (str.endsWith(includeName) && str[str.size()-includeName.size()-1] == QLatin1Char('/'))) { return {TopDUContextPointer((*iterator).data()), wordRange}; } ++iterator; } } return {{}, KTextEditor::Range::invalid()}; } QPair macroExpansionForPosition(const QUrl &url, const KTextEditor::Cursor& position) { TopDUContext* topContext = DUChainUtils::standardContextForUrl(url); if (topContext) { int useAt = topContext->findUseAt(topContext->transformToLocalRevision(position)); if (useAt >= 0) { Use use = topContext->uses()[useAt]; if (dynamic_cast(use.usedDeclaration(topContext))) { return {TopDUContextPointer(topContext), use}; } } } return {{}, Use()}; } } ClangSupport::ClangSupport(QObject* parent, const QVariantList& ) : IPlugin( QStringLiteral("kdevclangsupport"), parent ) , ILanguageSupport() , m_highlighting(nullptr) , m_refactoring(nullptr) , m_index(nullptr) { KDEV_USE_EXTENSION_INTERFACE( KDevelop::ILanguageSupport ) setXMLFile( QStringLiteral("kdevclangsupport.rc") ); ClangIntegration::DUChainUtils::registerDUChainItems(); m_highlighting = new ClangHighlighting(this); m_refactoring = new BasicRefactoring(this); m_index.reset(new ClangIndex); - new KDevelop::CodeCompletion( this, new ClangCodeCompletionModel(m_index.data(), this), name() ); + auto model = new KDevelop::CodeCompletion( this, new ClangCodeCompletionModel(m_index.data(), this), name() ); + // TODO: use direct signal/slot connect syntax for 5.1 + connect(model, SIGNAL(registeredToView(KTextEditor::View*)), + this, SLOT(disableKeywordCompletion(KTextEditor::View*))); + connect(model, SIGNAL(unregisteredFromView(KTextEditor::View*)), + this, SLOT(enableKeywordCompletion(KTextEditor::View*))); for(const auto& type : DocumentFinderHelpers::mimeTypesList()){ KDevelop::IBuddyDocumentFinder::addFinder(type, this); } auto assistantsManager = core()->languageController()->staticAssistantsManager(); assistantsManager->registerAssistant(StaticAssistant::Ptr(new RenameAssistant(this))); assistantsManager->registerAssistant(StaticAssistant::Ptr(new AdaptSignatureAssistant(this))); connect(ICore::self()->documentController(), &IDocumentController::documentActivated, this, &ClangSupport::documentActivated); } ClangSupport::~ClangSupport() { parseLock()->lockForWrite(); // By locking the parse-mutexes, we make sure that parse jobs get a chance to finish in a good state parseLock()->unlock(); for(const auto& type : DocumentFinderHelpers::mimeTypesList()) { KDevelop::IBuddyDocumentFinder::removeFinder(type); } ClangIntegration::DUChainUtils::unregisterDUChainItems(); } KDevelop::ConfigPage* ClangSupport::configPage(int number, QWidget* parent) { return number == 0 ? new SessionSettings(parent) : nullptr; } int ClangSupport::configPages() const { return 1; } ParseJob* ClangSupport::createParseJob(const IndexedString& url) { return new ClangParseJob(url, this); } QString ClangSupport::name() const { return QStringLiteral("clang"); } ICodeHighlighting* ClangSupport::codeHighlighting() const { return m_highlighting; } BasicRefactoring* ClangSupport::refactoring() const { return m_refactoring; } ClangIndex* ClangSupport::index() { return m_index.data(); } bool ClangSupport::areBuddies(const QUrl &url1, const QUrl& url2) { return DocumentFinderHelpers::areBuddies(url1, url2); } bool ClangSupport::buddyOrder(const QUrl &url1, const QUrl& url2) { return DocumentFinderHelpers::buddyOrder(url1, url2); } QVector< QUrl > ClangSupport::getPotentialBuddies(const QUrl &url) const { return DocumentFinderHelpers::getPotentialBuddies(url); } void ClangSupport::createActionsForMainWindow (Sublime::MainWindow* /*window*/, QString& _xmlFile, KActionCollection& actions) { _xmlFile = xmlFile(); QAction* renameDeclarationAction = actions.addAction(QStringLiteral("code_rename_declaration")); renameDeclarationAction->setText( i18n("Rename Declaration") ); renameDeclarationAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); actions.setDefaultShortcut(renameDeclarationAction, Qt::CTRL | Qt::SHIFT | Qt::Key_R); connect(renameDeclarationAction, &QAction::triggered, m_refactoring, &BasicRefactoring::executeRenameAction); } KDevelop::ContextMenuExtension ClangSupport::contextMenuExtension(KDevelop::Context* context) { ContextMenuExtension cm; EditorContext *ec = dynamic_cast(context); if (ec && ICore::self()->languageController()->languagesForUrl(ec->url()).contains(this)) { // It's a C++ file, let's add our context menu. m_refactoring->fillContextMenu(cm, context); } return cm; } KTextEditor::Range ClangSupport::specialLanguageObjectRange(const QUrl &url, const KTextEditor::Cursor& position) { DUChainReadLocker lock; const QPair macroExpansion = macroExpansionForPosition(url, position); if (macroExpansion.first) { return macroExpansion.first->transformFromLocalRevision(macroExpansion.second.m_range); } const QPair import = importedContextForPosition(url, position); if(import.first) { return import.second; } return KTextEditor::Range::invalid(); } QPair ClangSupport::specialLanguageObjectJumpCursor(const QUrl &url, const KTextEditor::Cursor& position) { const QPair import = importedContextForPosition(url, position); DUChainReadLocker lock; if (import.first) { return qMakePair(import.first->url().toUrl(), KTextEditor::Cursor(0,0)); } return {{}, KTextEditor::Cursor::invalid()}; } QWidget* ClangSupport::specialLanguageObjectNavigationWidget(const QUrl &url, const KTextEditor::Cursor& position) { DUChainReadLocker lock; const QPair macroExpansion = macroExpansionForPosition(url, position); if (macroExpansion.first) { Declaration* declaration = macroExpansion.second.usedDeclaration(macroExpansion.first.data()); const MacroDefinition::Ptr macroDefinition(dynamic_cast(declaration)); Q_ASSERT(macroDefinition); auto rangeInRevision = macroExpansion.first->transformFromLocalRevision(macroExpansion.second.m_range.start); return new ClangNavigationWidget(macroDefinition, DocumentCursor(IndexedString(url), rangeInRevision)); } const QPair import = importedContextForPosition(url, position); if (import.first) { return import.first->createNavigationWidget(); } return nullptr; } TopDUContext* ClangSupport::standardContext(const QUrl &url, bool /*proxyContext*/) { ClangParsingEnvironment env; return DUChain::self()->chainForDocument(url, &env); } void ClangSupport::documentActivated(IDocument* doc) { TopDUContext::Features features; { DUChainReadLocker lock; auto ctx = DUChainUtils::standardContextForUrl(doc->url()); if (!ctx) { return; } auto file = ctx->parsingEnvironmentFile(); if (!file) { return; } if (file->type() != CppParsingEnvironment) { return; } if (file->needsUpdate()) { return; } features = ctx->features(); } const auto indexedUrl = IndexedString(doc->url()); auto sessionData = ClangIntegration::DUChainUtils::findParseSessionData(indexedUrl, index()->translationUnitForUrl(IndexedString(doc->url()))); if (sessionData) { return; } if ((features & TopDUContext::AllDeclarationsContextsAndUses) != TopDUContext::AllDeclarationsContextsAndUses) { // the file was parsed in simplified mode, we need to reparse it to get all data // now that its opened in the editor features = TopDUContext::AllDeclarationsContextsAndUses; } else { features = static_cast(ClangParseJob::AttachASTWithoutUpdating | features); if (ICore::self()->languageController()->backgroundParser()->isQueued(indexedUrl)) { // The document is already scheduled for parsing (happens when opening a project with an active document) // The background parser will optimize the previous request out, so we need to update highlighting features = static_cast(ClangParseJob::UpdateHighlighting | features); } } ICore::self()->languageController()->backgroundParser()->addDocument(indexedUrl, features); } +static void setKeywordCompletion(KTextEditor::View* view, bool enabled) +{ + if (auto config = qobject_cast(view)) { + config->setConfigValue(QStringLiteral("keyword-completion"), enabled); + } +} + +void ClangSupport::disableKeywordCompletion(KTextEditor::View* view) +{ + setKeywordCompletion(view, false); +} + +void ClangSupport::enableKeywordCompletion(KTextEditor::View* view) +{ + setKeywordCompletion(view, true); +} + #include "clangsupport.moc" diff --git a/languages/clang/clangsupport.h b/languages/clang/clangsupport.h index ab5192bf4f..8aae363242 100644 --- a/languages/clang/clangsupport.h +++ b/languages/clang/clangsupport.h @@ -1,93 +1,100 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff Copyright 2014 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVCLANGSUPPORT_H #define KDEVCLANGSUPPORT_H #include #include #include #include #include class ClangIndex; namespace KDevelop { class BasicRefactoring; class IDocument; } +namespace KTextEditor +{ +class View; +} + class ClangSupport : public KDevelop::IPlugin, public KDevelop::ILanguageSupport, public KDevelop::IBuddyDocumentFinder { Q_OBJECT Q_INTERFACES(KDevelop::ILanguageSupport) public: explicit ClangSupport(QObject *parent, const QVariantList& args = QVariantList()); virtual ~ClangSupport(); /** Name Of the Language */ QString name() const override; /** Parsejob used by background parser to parse given url */ KDevelop::ParseJob *createParseJob(const KDevelop::IndexedString &url) override; /** the code highlighter */ KDevelop::ICodeHighlighting* codeHighlighting() const override; KDevelop::BasicRefactoring* refactoring() const override; void createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions) override; KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; KTextEditor::Range specialLanguageObjectRange(const QUrl &url, const KTextEditor::Cursor& position) override; QPair specialLanguageObjectJumpCursor(const QUrl &url, const KTextEditor::Cursor& position) override; QWidget* specialLanguageObjectNavigationWidget(const QUrl &url, const KTextEditor::Cursor& position) override; ClangIndex* index(); KDevelop::TopDUContext* standardContext(const QUrl &url, bool proxyContext = false) override; virtual KDevelop::ConfigPage* configPage(int number, QWidget *parent) override; virtual int configPages() const override; //BEGIN IBuddyDocumentFinder bool areBuddies(const QUrl &url1, const QUrl& url2) override; bool buddyOrder(const QUrl &url1, const QUrl& url2) override; QVector< QUrl > getPotentialBuddies(const QUrl &url) const override; //END IBuddyDocumentFinder private slots: void documentActivated(KDevelop::IDocument* doc); + void disableKeywordCompletion(KTextEditor::View* view); + void enableKeywordCompletion(KTextEditor::View* view); private: KDevelop::ICodeHighlighting *m_highlighting; KDevelop::BasicRefactoring *m_refactoring; QScopedPointer m_index; }; #endif diff --git a/languages/clang/codecompletion/completionhelper.cpp b/languages/clang/codecompletion/completionhelper.cpp index ac307eff27..8bc1c034ff 100644 --- a/languages/clang/codecompletion/completionhelper.cpp +++ b/languages/clang/codecompletion/completionhelper.cpp @@ -1,407 +1,389 @@ /* * This file is part of KDevelop * Copyright 2014 David Stevens * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "completionhelper.h" #include "../duchain/cursorkindtraits.h" #include "../duchain/parsesession.h" #include "../duchain/documentfinderhelpers.h" #include "../duchain/clanghelpers.h" #include "../util/clangdebug.h" #include "../util/clangtypes.h" #include "../util/clangutils.h" #include #include #include namespace { struct OverrideInfo { FunctionOverrideList* functions; QStringList templateTypes; QMap templateTypeMap; }; struct ImplementsInfo { CXCursor origin; CXCursor top; FunctionImplementsList* prototypes; QVector originScope; QVector fileFilter; int depth; QString templatePrefix; - QString scopePrefix; }; -constexpr bool canContainFunctionDecls(CXCursorKind kind) -{ - return kind == CXCursor_Namespace || kind == CXCursor_StructDecl || - kind == CXCursor_UnionDecl || kind == CXCursor_ClassDecl || - kind == CXCursor_ClassTemplate || kind == CXCursor_ClassTemplatePartialSpecialization; -} - //TODO replace this with clang_Type_getTemplateArgumentAsType when that //function makes it into the mainstream libclang release. QStringList templateTypeArguments(CXCursor cursor) { QStringList types; QString tStr = ClangString(clang_getTypeSpelling(clang_getCursorType(cursor))).toString(); ParamIterator iter(QStringLiteral("<>"), tStr); while (iter) { types.append(*iter); ++iter; } return types; } CXChildVisitResult templateParamsHelper(CXCursor cursor, CXCursor /*parent*/, CXClientData data) { CXCursorKind kind = clang_getCursorKind(cursor); if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_TemplateTemplateParameter || kind == CXCursor_NonTypeTemplateParameter) { (*static_cast(data)).append(ClangString(clang_getCursorSpelling(cursor)).toString()); } return CXChildVisit_Continue; } QStringList templateParams(CXCursor cursor) { QStringList types; clang_visitChildren(cursor, templateParamsHelper, &types); return types; } FuncOverrideInfo processCXXMethod(CXCursor cursor, OverrideInfo* info) { QStringList params; int numArgs = clang_Cursor_getNumArguments(cursor); for (int i = 0; i < numArgs; i++) { CXCursor arg = clang_Cursor_getArgument(cursor, i); QString id = ClangString(clang_getCursorDisplayName(arg)).toString(); QString type = ClangString(clang_getTypeSpelling(clang_getCursorType(arg))).toString(); if (info->templateTypeMap.contains(type)) { type = info->templateTypeMap.value(type); } params << type + QLatin1Char(' ') + id; } FuncOverrideInfo fp; QString retType = ClangString(clang_getTypeSpelling(clang_getCursorResultType(cursor))).toString(); if (info->templateTypeMap.contains(retType)) { retType = info->templateTypeMap.value(retType); } fp.returnType = retType; fp.name = ClangString(clang_getCursorSpelling(cursor)).toString(); fp.params = params; fp.isVirtual = clang_CXXMethod_isPureVirtual(cursor); fp.isConst = clang_CXXMethod_isConst(cursor); return fp; } CXChildVisitResult baseClassVisitor(CXCursor cursor, CXCursor /*parent*/, CXClientData data); void processBaseClass(CXCursor cursor, FunctionOverrideList* functionList) { QStringList concrete; CXCursor ref = clang_getCursorReferenced(cursor); CXCursor isTemplate = clang_getSpecializedCursorTemplate(ref); if (!clang_Cursor_isNull(isTemplate)) { concrete = templateTypeArguments(ref); ref = isTemplate; } OverrideInfo info{functionList, concrete, {}}; clang_visitChildren(ref, baseClassVisitor, &info); } CXChildVisitResult baseClassVisitor(CXCursor cursor, CXCursor /*parent*/, CXClientData data) { QString templateParam; OverrideInfo* info = static_cast(data); switch(clang_getCursorKind(cursor)) { case CXCursor_TemplateTypeParameter: templateParam = ClangString(clang_getCursorSpelling(cursor)).toString(); // TODO: this is probably just a hotfix, find a proper solution to // https://bugs.kde.org/show_bug.cgi?id=355163 if (info->templateTypes.size() > info->templateTypeMap.size()) { info->templateTypeMap.insert(templateParam, info->templateTypes.at(info->templateTypeMap.size())); } return CXChildVisit_Continue; case CXCursor_CXXBaseSpecifier: processBaseClass(cursor, info->functions); return CXChildVisit_Continue; case CXCursor_CXXMethod: if (clang_CXXMethod_isVirtual(cursor)) { info->functions->append(processCXXMethod(cursor, info)); } return CXChildVisit_Continue; default: return CXChildVisit_Continue; } } CXChildVisitResult findBaseVisitor(CXCursor cursor, CXCursor /*parent*/, CXClientData data) { auto cursorKind = clang_getCursorKind(cursor); if (cursorKind == CXCursor_CXXBaseSpecifier) { processBaseClass(cursor, static_cast(data)); } else if (cursorKind == CXCursor_CXXMethod) { if (!clang_CXXMethod_isVirtual(cursor)) { return CXChildVisit_Continue; } auto info = static_cast(data); OverrideInfo overrideInfo {info, {}, {}}; auto methodInfo = processCXXMethod(cursor, &overrideInfo); if (info->contains(methodInfo)) { // This method is already implemented, remove it from the list of methods that can be overridden. info->remove(info->indexOf(methodInfo), 1); } } return CXChildVisit_Continue; } // TODO: make sure we only skip this in classes that actually inherit QObject bool isQtMocFunction(CXCursor cursor) { static const QByteArray mocFunctions[] = { QByteArrayLiteral("metaObject"), QByteArrayLiteral("qt_metacast"), QByteArrayLiteral("qt_metacall"), QByteArrayLiteral("qt_static_metacall"), }; const ClangString function(clang_getCursorSpelling(cursor)); auto it = std::find(std::begin(mocFunctions), std::end(mocFunctions), function.toByteArray()); if (it != std::end(mocFunctions)) { auto range = ClangRange(clang_getCursorExtent(cursor)).toRange(); // tokenizing the above range fails for some reason, but // if the function comes from a range that happens to be just as wide // as the expected Q_OBJECT macro, then we assume this is a moc function // and skip it. return range.onSingleLine() && range.columnWidth() == strlen("Q_OBJECT"); } return false; } CXChildVisitResult declVisitor(CXCursor cursor, CXCursor parent, CXClientData d) { CXCursorKind kind = clang_getCursorKind(cursor); struct ImplementsInfo* data = static_cast(d); auto location = clang_getCursorLocation(cursor); if (clang_Location_isInSystemHeader(location)) { // never offer implementation items for system headers // TODO: also filter out non-system files unrelated to the current file // e.g. based on the path or similar return CXChildVisit_Continue; } CXFile file = nullptr; clang_getFileLocation(location, &file, nullptr, nullptr, nullptr); if (!data->fileFilter.contains(file)) { return CXChildVisit_Continue; } //Recurse into cursors which could contain a function declaration - if (canContainFunctionDecls(kind)) { + if (ClangUtils::isScopeKind(kind)) { //Don't enter a scope that branches from the origin's scope if (data->depth < data->originScope.count() && !clang_equalCursors(cursor, data->originScope.at(data->depth))) { return CXChildVisit_Continue; } - QString part, templatePrefix; - if (data->depth >= data->originScope.count()) { - QString name = ClangString(clang_getCursorDisplayName(cursor)).toString(); - - //This code doesn't play well with anonymous namespaces, so don't recurse - //into them at all. TODO improve support for anonymous namespaces - if (kind == CXCursor_Namespace && name.isEmpty()) { - return CXChildVisit_Continue; - } + // we must not declare a function outside of its anonymous namespace, so + // don't recurse into anonymous namespaces if we are not in one already + if (kind == CXCursor_Namespace && !clang_equalCursors(data->origin, cursor) && ClangString(clang_getCursorDisplayName(cursor)).isEmpty()) { + return CXChildVisit_Continue; + } + QString templatePrefix; + if (data->depth >= data->originScope.count()) { if (kind == CXCursor_ClassTemplate || kind == CXCursor_ClassTemplatePartialSpecialization) { - part = name + QLatin1String("::"); - //If we're at a template, we need to construct the template //which goes at the front of the prototype QStringList templateTypes = templateParams(kind == CXCursor_ClassTemplate ? cursor : clang_getSpecializedCursorTemplate(cursor)); templatePrefix = QLatin1String("template<"); for (int i = 0; i < templateTypes.count(); i++) { templatePrefix = templatePrefix + QLatin1String((i > 0) ? ", " : "") + QLatin1String("typename ") + templateTypes.at(i); } templatePrefix = templatePrefix + QLatin1String("> "); - } else { - part = name + QLatin1String("::"); } } ImplementsInfo info{data->origin, data->top, data->prototypes, data->originScope, data->fileFilter, data->depth + 1, - data->templatePrefix + templatePrefix, - data->scopePrefix + part}; + data->templatePrefix + templatePrefix}; clang_visitChildren(cursor, declVisitor, &info); return CXChildVisit_Continue; } if (data->depth < data->originScope.count()) { return CXChildVisit_Continue; } //If the current cursor is not a function or if it is already defined, there's nothing to do here if (!CursorKindTraits::isFunction(clang_getCursorKind(cursor)) || !clang_equalCursors(clang_getNullCursor(), clang_getCursorDefinition(cursor))) { return CXChildVisit_Continue; } CXCursor origin = data->origin; //Don't try to redefine class/structure/union members if (clang_equalCursors(origin, parent) && (clang_getCursorKind(origin) != CXCursor_Namespace && !clang_equalCursors(origin, data->top))) { return CXChildVisit_Continue; } // skip explicitly defaulted/deleted functions as they don't need a definition if (ClangUtils::isExplicitlyDefaultedOrDeleted(cursor)) { return CXChildVisit_Continue; } if (isQtMocFunction(cursor)) { return CXChildVisit_Continue; } //TODO Add support for pure virtual functions - auto scope = data->scopePrefix; - if (scope.endsWith(QLatin1String("::"))) { - scope.chop(2); // chop '::' - } + const auto scope = ClangUtils::getScope(cursor, data->origin); QString signature = ClangUtils::getCursorSignature(cursor, scope); QString returnType, rest; if (kind != CXCursor_Constructor && kind != CXCursor_Destructor) { int spaceIndex = signature.indexOf(QLatin1Char(' ')); returnType = signature.left(spaceIndex); rest = signature.right(signature.count() - spaceIndex - 1); } else { rest = signature; } //TODO Add support for pure virtual functions ReferencedTopDUContext top; { DUChainReadLocker lock; top = DUChain::self()->chainForDocument(ClangString(clang_getFileName(file)).toIndexed()); } DeclarationPointer declaration = ClangHelpers::findDeclaration(clang_getCursorLocation(cursor), top); data->prototypes->append(FuncImplementInfo{kind == CXCursor_Constructor, kind == CXCursor_Destructor, data->templatePrefix, returnType, rest, declaration}); return CXChildVisit_Continue; } } bool FuncOverrideInfo::operator==(const FuncOverrideInfo& rhs) const { return std::make_tuple(returnType, name, params, isConst) == std::make_tuple(rhs.returnType, rhs.name, rhs.params, rhs.isConst); } CompletionHelper::CompletionHelper() { } void CompletionHelper::computeCompletions(const ParseSession& session, CXFile file, const KTextEditor::Cursor& position) { const auto unit = session.unit(); CXSourceLocation location = clang_getLocation(unit, file, position.line() + 1, position.column() + 1); if (clang_equalLocations(clang_getNullLocation(), location)) { clangDebug() << "Completion helper given invalid position " << position << " in file " << clang_getFileName(file); return; } CXCursor topCursor = clang_getTranslationUnitCursor(unit); CXCursor currentCursor = clang_getCursor(unit, location); if (clang_getCursorKind(currentCursor) == CXCursor_NoDeclFound) { currentCursor = topCursor; } clang_visitChildren(currentCursor, findBaseVisitor, &m_overrides); if (clang_getCursorKind(currentCursor) == CXCursor_Namespace || clang_equalCursors(topCursor, currentCursor)) { QVector scopes; if (!clang_equalCursors(topCursor, currentCursor)) { CXCursor search = currentCursor; while (!clang_equalCursors(search, topCursor)) { scopes.append(clang_getCanonicalCursor(search)); search = clang_getCursorSemanticParent(search); } std::reverse(scopes.begin(), scopes.end()); } QVector fileFilter; fileFilter << file; const auto& buddies = DocumentFinderHelpers::getPotentialBuddies(QUrl::fromLocalFile(ClangString(clang_getFileName(file)).toString())); foreach(const auto& buddy, buddies) { auto buddyFile = clang_getFile(unit, qPrintable(buddy.toLocalFile())); if (buddyFile) { fileFilter << buddyFile; } } - ImplementsInfo info{currentCursor, topCursor, &m_implements, scopes, fileFilter, 0, QString(), QString()}; + ImplementsInfo info{currentCursor, topCursor, &m_implements, scopes, fileFilter, 0, QString()}; clang_visitChildren(topCursor, declVisitor, &info); } } FunctionOverrideList CompletionHelper::overrides() const { return m_overrides; } FunctionImplementsList CompletionHelper::implements() const { return m_implements; } diff --git a/languages/clang/duchain/CMakeLists.txt b/languages/clang/duchain/CMakeLists.txt index ca4a83a0e9..4aa630ed8c 100644 --- a/languages/clang/duchain/CMakeLists.txt +++ b/languages/clang/duchain/CMakeLists.txt @@ -1,42 +1,44 @@ add_library(kdevclangduchain STATIC parsesession.cpp clangdiagnosticevaluator.cpp clangducontext.cpp clangindex.cpp clangparsingenvironmentfile.cpp clangparsingenvironment.cpp clangproblem.cpp debugvisitor.cpp duchainutils.cpp builder.cpp clangpch.cpp clanghelpers.cpp unknowndeclarationproblem.cpp macrodefinition.cpp missingincludepathproblem.cpp macronavigationcontext.cpp navigationwidget.cpp todoextractor.cpp types/classspecializationtype.cpp unsavedfile.cpp documentfinderhelpers.cpp ) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) generate_export_header(kdevclangduchain EXPORT_FILE_NAME clangduchainexport.h) target_link_libraries(kdevclangduchain LINK_PRIVATE Qt5::Core kdevclangcodegen kdevclangcodecompletion kdevclangutil settingsmanager LINK_PUBLIC KDev::Language KDev::Project KDev::Util ${CLANG_CLANG_LIB} ) + +install(FILES gcc_compat.h DESTINATION ${DATA_INSTALL_DIR}/kdevclangsupport PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) \ No newline at end of file diff --git a/languages/clang/duchain/builder.cpp b/languages/clang/duchain/builder.cpp index b8760b98fd..831b5a0e40 100644 --- a/languages/clang/duchain/builder.cpp +++ b/languages/clang/duchain/builder.cpp @@ -1,1416 +1,1430 @@ /* * This file is part of KDevelop * * Copyright 2013 Olivier de Gaalon * Copyright 2015 Milian Wolff * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "builder.h" #include "util/clangdebug.h" #include "templatehelpers.h" #include "cursorkindtraits.h" #include "clangducontext.h" #include "macrodefinition.h" #include "types/classspecializationtype.h" #include "util/clangdebug.h" #include "util/clangutils.h" #include "util/clangtypes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// Turn on for debugging the declaration building #define IF_DEBUG(x) namespace { // TODO: this is ugly, can we find a better alternative? static bool s_jsonTestRun = false; //BEGIN helpers /** * Find the cursor that cursor @p cursor references * * First tries to get the referenced cursor via clang_getCursorReferenced, * and if that fails, tries to get them via clang_getOverloadedDecl * (which returns the referenced cursor for CXCursor_OverloadedDeclRef, for example) * * @return Valid cursor on success, else null cursor */ CXCursor referencedCursor(CXCursor cursor) { auto referenced = clang_getCursorReferenced(cursor); if (!clang_equalCursors(cursor, referenced)) { return referenced; } // get the first result for now referenced = clang_getOverloadedDecl(cursor, 0); if (!clang_Cursor_isNull(referenced)) { return referenced; } return clang_getNullCursor(); } Identifier makeId(CXCursor cursor) { return Identifier(ClangString(clang_getCursorSpelling(cursor)).toIndexed()); } QByteArray makeComment(CXComment comment) { if (Q_UNLIKELY(s_jsonTestRun)) { auto kind = clang_Comment_getKind(comment); if (kind == CXComment_Text) return ClangString(clang_TextComment_getText(comment)).toByteArray(); QByteArray text; int numChildren = clang_Comment_getNumChildren(comment); for (int i = 0; i < numChildren; ++i) text += makeComment(clang_Comment_getChild(comment, i)); return text; } return ClangString(clang_FullComment_getAsHTML(comment)).toByteArray(); } AbstractType* createDelayedType(CXType type) { auto t = new DelayedType; QString typeName = ClangString(clang_getTypeSpelling(type)).toString(); typeName.remove(QStringLiteral("const ")); typeName.remove(QStringLiteral("volatile ")); t->setIdentifier(IndexedTypeIdentifier(typeName)); return t; } void contextImportDecl(DUContext* context, const DeclarationPointer& decl) { auto top = context->topContext(); if (auto import = decl->logicalInternalContext(top)) { context->addImportedParentContext(import); context->topContext()->updateImportsCache(); } } //END helpers CXChildVisitResult visitCursor(CXCursor cursor, CXCursor parent, CXClientData data); //BEGIN IdType template struct IdType; template struct IdType::type> { typedef StructureType Type; }; template struct IdType::type> { typedef TypeAliasType Type; }; template struct IdType::type> { typedef TypeAliasType Type; }; template struct IdType::type> { typedef EnumerationType Type; }; template struct IdType::type> { typedef EnumeratorType Type; }; //END IdType //BEGIN DeclType template struct DeclType; template struct DeclType::type> { typedef Declaration Type; }; template struct DeclType::type> { typedef MacroDefinition Type; }; template struct DeclType::type> { typedef ForwardDeclaration Type; }; template struct DeclType::type> { typedef ClassDeclaration Type; }; template struct DeclType::type> { typedef ClassFunctionDeclaration Type; }; template struct DeclType::type> { typedef FunctionDeclaration Type; }; template struct DeclType::type> { typedef FunctionDefinition Type; }; template struct DeclType::type> { typedef NamespaceAliasDeclaration Type; }; template struct DeclType::type> { typedef ClassMemberDeclaration Type; }; //END DeclType //BEGIN CurrentContext struct CurrentContext { CurrentContext(DUContext* context) : context(context) { DUChainReadLocker lock; previousChildContexts = context->childContexts(); previousChildDeclarations = context->localDeclarations(); } ~CurrentContext() { DUChainWriteLocker lock; qDeleteAll(previousChildContexts); qDeleteAll(previousChildDeclarations); } DUContext* context; // when updatig, this contains child contexts of the current parent context QVector previousChildContexts; // when updatig, this contains child declarations of the current parent context QVector previousChildDeclarations; }; //END CurrentContext //BEGIN Visitor struct Visitor { explicit Visitor(CXTranslationUnit tu, CXFile file, const IncludeFileContexts& includes, const bool update); AbstractType *makeType(CXType type, CXCursor parent); AbstractType::Ptr makeAbsType(CXType type, CXCursor parent) { return AbstractType::Ptr(makeType(type, parent)); } //BEGIN dispatch* template = dummy> CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent); template = dummy> CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent); template = dummy> CXChildVisitResult dispatchCursor(CXCursor cursor, CXCursor parent); template AbstractType *dispatchType(CXType type, CXCursor cursor) { IF_DEBUG(clangDebug() << "TK:" << type.kind;) auto kdevType = createType(type, cursor); if (kdevType) { setTypeModifiers(type, kdevType); } return kdevType; } //BEGIN dispatch* //BEGIN build* template CXChildVisitResult buildDeclaration(CXCursor cursor); CXChildVisitResult buildUse(CXCursor cursor); CXChildVisitResult buildMacroExpansion(CXCursor cursor); CXChildVisitResult buildCompoundStatement(CXCursor cursor); CXChildVisitResult buildCXXBaseSpecifier(CXCursor cursor); CXChildVisitResult buildParmDecl(CXCursor cursor); //END build* //BEGIN create* template DeclType* createDeclarationCommon(CXCursor cursor, const Identifier& id) { auto range = ClangHelpers::cursorSpellingNameRange(cursor, id); if (id.isEmpty()) { // This is either an anonymous function parameter e.g.: void f(int); // Or anonymous struct/class/union e.g.: struct {} anonymous; // Set empty range for it range.end = range.start; } // check if cursor is inside a macro expansion auto clangRange = clang_Cursor_getSpellingNameRange(cursor, 0, 0); unsigned int expansionLocOffset; const auto spellingLocation = clang_getRangeStart(clangRange); clang_getExpansionLocation(spellingLocation, nullptr, nullptr, nullptr, &expansionLocOffset); if (m_macroExpansionLocations.contains(expansionLocOffset)) { unsigned int spellingLocOffset; clang_getSpellingLocation(spellingLocation, nullptr, nullptr, nullptr, &spellingLocOffset); // Set empty ranges for declarations inside macro expansion if (spellingLocOffset == expansionLocOffset) { range.end = range.start; } } if (m_update) { const IndexedIdentifier indexedId(id); DUChainWriteLocker lock; auto it = m_parentContext->previousChildDeclarations.begin(); while (it != m_parentContext->previousChildDeclarations.end()) { auto decl = dynamic_cast(*it); if (decl && decl->indexedIdentifier() == indexedId) { decl->setRange(range); setDeclData(cursor, decl); m_cursorToDeclarationCache[cursor] = decl; m_parentContext->previousChildDeclarations.erase(it); return decl; } ++it; } } auto decl = new DeclType(range, nullptr); decl->setIdentifier(id); m_cursorToDeclarationCache[cursor] = decl; setDeclData(cursor, decl); { DUChainWriteLocker lock; decl->setContext(m_parentContext->context); } return decl; } template Declaration* createDeclaration(CXCursor cursor, const Identifier& id, DUContext *context) { auto decl = createDeclarationCommon(cursor, id); auto type = createType(cursor); DUChainWriteLocker lock; if (context) decl->setInternalContext(context); setDeclType(decl, type); setDeclInCtxtData(cursor, decl); return decl; } template DUContext* createContext(CXCursor cursor, const QualifiedIdentifier& scopeId = {}) { // wtf: why is the DUContext API requesting a QID when it needs a plain Id?! // see: testNamespace auto range = ClangRange(clang_getCursorExtent(cursor)).toRangeInRevision(); DUChainWriteLocker lock; if (m_update) { const IndexedQualifiedIdentifier indexedScopeId(scopeId); auto it = m_parentContext->previousChildContexts.begin(); while (it != m_parentContext->previousChildContexts.end()) { auto ctx = *it; if (ctx->type() == Type && ctx->indexedLocalScopeIdentifier() == indexedScopeId) { ctx->setRange(range); m_parentContext->previousChildContexts.erase(it); return ctx; } ++it; } } //TODO: (..type, id..) constructor for DUContext? auto context = new ClangNormalDUContext(range, m_parentContext->context); context->setType(Type); context->setLocalScopeIdentifier(scopeId); if (Type == DUContext::Other || Type == DUContext::Function) context->setInSymbolTable(false); if (CK == CXCursor_CXXMethod || CursorKindTraits::isClass(CK)) { CXCursor semParent = clang_getCursorSemanticParent(cursor); if (!clang_Cursor_isNull(semParent)) { auto semParentDecl = findDeclaration(semParent); if (semParentDecl) { contextImportDecl(context, semParentDecl); } } } return context; } template = dummy> AbstractType *createType(CXType, CXCursor) { // TODO: would be nice to instantiate a ConstantIntegralType here and set a value if possible // but unfortunately libclang doesn't offer API to that // also see http://marc.info/?l=cfe-commits&m=131609142917881&w=2 return new IntegralType(CursorKindTraits::integralType(TK)); } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto ptr = new PointerType; ptr->setBaseType(makeAbsType(clang_getPointeeType(type), parent)); return ptr; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto arr = new ArrayType; arr->setDimension((TK == CXType_IncompleteArray || TK == CXType_VariableArray || TK == CXType_DependentSizedArray) ? 0 : clang_getArraySize(type)); arr->setElementType(makeAbsType(clang_getArrayElementType(type), parent)); return arr; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto ref = new ReferenceType; ref->setIsRValue(type.kind == CXType_RValueReference); ref->setBaseType(makeAbsType(clang_getPointeeType(type), parent)); return ref; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto func = new FunctionType; func->setReturnType(makeAbsType(clang_getResultType(type), parent)); const int numArgs = clang_getNumArgTypes(type); for (int i = 0; i < numArgs; ++i) { func->addArgument(makeAbsType(clang_getArgType(type, i), parent)); } if (clang_isFunctionTypeVariadic(type)) { auto type = new DelayedType; static const auto id = IndexedTypeIdentifier(QStringLiteral("...")); type->setIdentifier(id); type->setKind(DelayedType::Unresolved); func->addArgument(AbstractType::Ptr(type)); } return func; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { DeclarationPointer decl = findDeclaration(clang_getTypeDeclaration(type)); DUChainReadLocker lock; if (!decl) { // probably a forward-declared type decl = ClangHelpers::findForwardDeclaration(type, m_parentContext->context, parent); } if (clang_Type_getNumTemplateArguments(type) != -1) { return createClassTemplateSpecializationType(type, decl); } auto t = new StructureType; t->setDeclaration(decl.data()); return t; } template = dummy> AbstractType *createType(CXType type, CXCursor) { auto t = new EnumerationType; setIdTypeDecl(clang_getTypeDeclaration(type), t); return t; } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto t = new TypeAliasType; CXCursor location = clang_getTypeDeclaration(type); t->setType(makeAbsType(clang_getTypedefDeclUnderlyingType(location), parent)); setIdTypeDecl(location, t); return t; } template = dummy> AbstractType *createType(CXType, CXCursor /*parent*/) { auto t = new DelayedType; static const IndexedTypeIdentifier id(QLatin1String(CursorKindTraits::delayedTypeName(TK))); t->setIdentifier(id); return t; } template = dummy> AbstractType *createType(CXType type, CXCursor /*parent*/) { return createDelayedType(type); } template = dummy> AbstractType *createType(CXType type, CXCursor parent) { auto numTA = clang_Type_getNumTemplateArguments(type); if (numTA != -1) { // This is a class template specialization. return createClassTemplateSpecializationType(type); } // Maybe it's the ElaboratedType. E.g.: "struct Type foo();" or "NS::Type foo();" or "void foo(enum Enum e);" e.t.c. auto oldType = type; type = clang_getCanonicalType(type); bool isElaboratedType = type.kind != CXType_FunctionProto && type.kind != CXType_FunctionNoProto && type.kind != CXType_Unexposed && type.kind != CXType_Invalid && type.kind != CXType_Record; return !isElaboratedType ? createDelayedType(oldType) : makeType(type, parent); } template = dummy> typename IdType::Type *createType(CXCursor) { return new typename IdType::Type; } template = dummy> EnumeratorType *createType(CXCursor cursor) { auto type = new EnumeratorType; type->setValue(clang_getEnumConstantDeclUnsignedValue(cursor)); return type; } template = dummy> TypeAliasType *createType(CXCursor cursor) { auto type = new TypeAliasType; type->setType(makeAbsType(clang_getTypedefDeclUnderlyingType(cursor), cursor)); return type; } template = dummy> AbstractType* createType(CXCursor cursor) { auto clangType = clang_getCursorType(cursor); #if CINDEX_VERSION_MINOR < 31 if (clangType.kind == CXType_Unexposed) { // Clang sometimes can return CXType_Unexposed for CXType_FunctionProto kind. E.g. if it's AttributedType. return dispatchType(clangType, cursor); } #endif return makeType(clangType, cursor); } template = dummy> AbstractType *createType(CXCursor) { auto t = new DelayedType; static const IndexedTypeIdentifier id(QStringLiteral("Label")); t->setIdentifier(id); return t; } template = dummy> AbstractType *createType(CXCursor cursor) { auto clangType = clang_getCursorType(cursor); return makeType(clangType, cursor); } +#if CINDEX_VERSION_MINOR >= 31 + template = dummy> + AbstractType *createType(CXType type, CXCursor parent) + { + auto deducedType = clang_getCanonicalType(type); + bool isDeduced = deducedType.kind != CXType_Invalid && deducedType.kind != CXType_Auto; + + return !isDeduced ? createDelayedType(type) : makeType(deducedType, parent); + } +#endif + /// @param declaration an optional declaration that will be associated with created type AbstractType* createClassTemplateSpecializationType(CXType type, const DeclarationPointer declaration = {}) { auto numTA = clang_Type_getNumTemplateArguments(type); Q_ASSERT(numTA != -1); auto typeDecl = clang_getTypeDeclaration(type); if (!declaration && typeDecl.kind == CXCursor_NoDeclFound) { // clang_getTypeDeclaration doesn't handle all types, fall back to delayed type... return createDelayedType(type); } QStringList typesStr; QString tStr = ClangString(clang_getTypeSpelling(type)).toString(); ParamIterator iter(QStringLiteral("<>"), tStr); while (iter) { typesStr.append(*iter); ++iter; } auto cst = new ClassSpecializationType; for (int i = 0; i < numTA; i++) { auto argumentType = clang_Type_getTemplateArgumentAsType(type, i); AbstractType::Ptr currentType; if (argumentType.kind == CXType_Invalid) { if(i >= typesStr.size()){ currentType = createDelayedType(argumentType); } else { auto t = new DelayedType; t->setIdentifier(IndexedTypeIdentifier(typesStr[i])); currentType = t; } } else { currentType = makeType(argumentType, typeDecl); } if (currentType) { cst->addParameter(currentType->indexed()); } } auto decl = declaration ? declaration : findDeclaration(typeDecl); DUChainReadLocker lock; cst->setDeclaration(decl.data()); return cst; } //END create* //BEGIN setDeclData template void setDeclData(CXCursor cursor, Declaration *decl, bool setComment = true) const; template void setDeclData(CXCursor cursor, MacroDefinition* decl) const; template void setDeclData(CXCursor cursor, ClassMemberDeclaration *decl) const; template = dummy> void setDeclData(CXCursor cursor, ClassDeclaration* decl) const; template = dummy> void setDeclData(CXCursor cursor, ClassDeclaration* decl) const; template void setDeclData(CXCursor cursor, AbstractFunctionDeclaration* decl) const; template void setDeclData(CXCursor cursor, ClassFunctionDeclaration* decl) const; template void setDeclData(CXCursor cursor, FunctionDeclaration *decl, bool setComment = true) const; template void setDeclData(CXCursor cursor, FunctionDefinition *decl) const; template void setDeclData(CXCursor cursor, NamespaceAliasDeclaration *decl) const; //END setDeclData //BEGIN setDeclInCtxtData template void setDeclInCtxtData(CXCursor, Declaration*) { //No-op } template void setDeclInCtxtData(CXCursor cursor, ClassFunctionDeclaration *decl) { // HACK to retrieve function-constness // This looks like a bug in Clang -- In theory setTypeModifiers should take care of setting the const modifier // however, clang_isConstQualifiedType() for TK == CXType_FunctionProto always returns false // TODO: Debug further auto type = decl->abstractType(); if (type) { if (clang_CXXMethod_isConst(cursor)) { type->setModifiers(type->modifiers() | AbstractType::ConstModifier); decl->setAbstractType(type); } } } template void setDeclInCtxtData(CXCursor cursor, FunctionDefinition *def) { setDeclInCtxtData(cursor, static_cast(def)); const CXCursor canon = clang_getCanonicalCursor(cursor); if (auto decl = findDeclaration(canon)) { def->setDeclaration(decl.data()); } } //END setDeclInCtxtData //BEGIN setDeclType template void setDeclType(Declaration *decl, typename IdType::Type *type) { setDeclType(decl, static_cast(type)); setDeclType(decl, static_cast(type)); } template void setDeclType(Declaration *decl, IdentifiedType *type) { type->setDeclaration(decl); } template void setDeclType(Declaration *decl, AbstractType *type) { decl->setAbstractType(AbstractType::Ptr(type)); } //END setDeclType template void setTypeModifiers(CXType type, AbstractType* kdevType) const; const CXFile m_file; const IncludeFileContexts &m_includes; DeclarationPointer findDeclaration(CXCursor cursor) const; void setIdTypeDecl(CXCursor typeCursor, IdentifiedType* idType) const; std::unordered_map> m_uses; /// At these location offsets (cf. @ref clang_getExpansionLocation) we encountered macro expansions QSet m_macroExpansionLocations; mutable QHash m_cursorToDeclarationCache; CurrentContext *m_parentContext; const bool m_update; }; //BEGIN setTypeModifiers template void Visitor::setTypeModifiers(CXType type, AbstractType* kdevType) const { quint64 modifiers = 0; if (clang_isConstQualifiedType(type)) { modifiers |= AbstractType::ConstModifier; } if (clang_isVolatileQualifiedType(type)) { modifiers |= AbstractType::VolatileModifier; } if (TK == CXType_Short || TK == CXType_UShort) { modifiers |= AbstractType::ShortModifier; } if (TK == CXType_Long || TK == CXType_LongDouble || TK == CXType_ULong) { modifiers |= AbstractType::LongModifier; } if (TK == CXType_LongLong || TK == CXType_ULongLong) { modifiers |= AbstractType::LongLongModifier; } if (TK == CXType_SChar) { modifiers |= AbstractType::SignedModifier; } if (TK == CXType_UChar || TK == CXType_UInt || TK == CXType_UShort || TK == CXType_UInt128 || TK == CXType_ULong || TK == CXType_ULongLong) { modifiers |= AbstractType::UnsignedModifier; } kdevType->setModifiers(modifiers); } //END setTypeModifiers //BEGIN dispatchCursor template> CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent) { const bool decision = CursorKindTraits::isClass(clang_getCursorKind(parent)); return decision ? dispatchCursor(cursor, parent) : dispatchCursor(cursor, parent); } template> CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent) { IF_DEBUG(clangDebug() << "IsInClass:" << IsInClass << "- isDefinition:" << IsDefinition;) const bool isDefinition = clang_isCursorDefinition(cursor); return isDefinition ? dispatchCursor(cursor, parent) : dispatchCursor(cursor, parent); } template> CXChildVisitResult Visitor::dispatchCursor(CXCursor cursor, CXCursor parent) { IF_DEBUG(clangDebug() << "IsInClass:" << IsInClass << "- isDefinition:" << IsDefinition;) // We may end up visiting the same cursor twice in some cases // see discussion on https://git.reviewboard.kde.org/r/119526/ // TODO: Investigate why this is happening in libclang if ((CursorKindTraits::isClass(CK) || CK == CXCursor_EnumDecl) && clang_getCursorKind(parent) == CXCursor_VarDecl) { return CXChildVisit_Continue; } constexpr bool isClassMember = IsInClass == Decision::True; constexpr bool isDefinition = IsDefinition == Decision::True; constexpr bool hasContext = CursorKindTraits::isFunction(CK) || (IsDefinition == Decision::True); return buildDeclaration::Type, hasContext>(cursor); } //END dispatchCursor //BEGIN setDeclData template void Visitor::setDeclData(CXCursor cursor, Declaration *decl, bool setComment) const { if (setComment) decl->setComment(makeComment(clang_Cursor_getParsedComment(cursor))); if (CursorKindTraits::isAliasType(CK)) { decl->setIsTypeAlias(true); } if (CK == CXCursor_Namespace) decl->setKind(Declaration::Namespace); if (CK == CXCursor_EnumDecl || CK == CXCursor_EnumConstantDecl || CursorKindTraits::isClass(CK) || CursorKindTraits::isAliasType(CK)) decl->setKind(Declaration::Type); int isAlwaysDeprecated; clang_getCursorPlatformAvailability(cursor, &isAlwaysDeprecated, nullptr, nullptr, nullptr, nullptr, 0); decl->setDeprecated(isAlwaysDeprecated); } template void Visitor::setDeclData(CXCursor cursor, MacroDefinition* decl) const { setDeclData(cursor, static_cast(decl)); if (m_update) { decl->clearParameters(); } auto unit = clang_Cursor_getTranslationUnit(cursor); auto range = clang_getCursorExtent(cursor); // TODO: Quite lacking API in libclang here. // No way to find out if this macro is function-like or not // cf. http://clang.llvm.org/doxygen/classclang_1_1MacroInfo.html // And no way to get the actual definition text range // Should be quite easy to expose that in libclang, though // Let' still get some basic support for this and parse on our own, it's not that difficult const QString contents = QString::fromUtf8(ClangUtils::getRawContents(unit, range)); const int firstOpeningParen = contents.indexOf(QLatin1Char('(')); const int firstWhitespace = contents.indexOf(QLatin1Char(' ')); const bool isFunctionLike = (firstOpeningParen != -1) && (firstOpeningParen < firstWhitespace); decl->setFunctionLike(isFunctionLike); // now extract the actual definition text int start = -1; if (isFunctionLike) { const int closingParen = findClose(contents, firstOpeningParen); if (closingParen != -1) { start = closingParen + 2; // + ')' + ' ' // extract macro function parameters const QString parameters = contents.mid(firstOpeningParen, closingParen - firstOpeningParen + 1); ParamIterator paramIt(QStringLiteral("():"), parameters, 0); while (paramIt) { decl->addParameter(IndexedString(*paramIt)); ++paramIt; } } } else { start = firstWhitespace + 1; // + ' ' } if (start == -1) { // unlikely: invalid macro definition, insert the complete #define statement decl->setDefinition(IndexedString(QLatin1String("#define ") + contents)); } else if (start < contents.size()) { decl->setDefinition(IndexedString(contents.mid(start))); } // else: macro has no body => leave the definition text empty } template void Visitor::setDeclData(CXCursor cursor, ClassMemberDeclaration *decl) const { setDeclData(cursor, static_cast(decl)); //A CXCursor_VarDecl in a class is static (otherwise it'd be a CXCursor_FieldDecl) if (CK == CXCursor_VarDecl) decl->setStatic(true); decl->setAccessPolicy(CursorKindTraits::kdevAccessPolicy(clang_getCXXAccessSpecifier(cursor))); #if CINDEX_VERSION_MINOR >= 31 decl->setMutable(clang_CXXField_isMutable(cursor)); #endif #if CINDEX_VERSION_MINOR >= 30 if (!s_jsonTestRun) { auto offset = clang_Cursor_getOffsetOfField(cursor); if (offset >= 0) { // don't add this info to the json tests, it invalidates the comment structure auto type = clang_getCursorType(cursor); auto sizeOf = clang_Type_getSizeOf(type); auto alignedTo = clang_Type_getAlignOf(type); decl->setComment(decl->comment() + i18n("
offset in parent: %1 Bit
" "size: %2 Bytes
" "aligned to: %3 Bytes", offset, sizeOf, alignedTo).toUtf8()); } } #endif } template> void Visitor::setDeclData(CXCursor cursor, ClassDeclaration* decl) const { CXCursorKind kind = clang_getTemplateCursorKind(cursor); switch (kind) { case CXCursor_UnionDecl: setDeclData(cursor, decl); break; case CXCursor_StructDecl: setDeclData(cursor, decl); break; case CXCursor_ClassDecl: setDeclData(cursor, decl); break; default: Q_ASSERT(false); break; } } template> void Visitor::setDeclData(CXCursor cursor, ClassDeclaration* decl) const { if (m_update) { decl->clearBaseClasses(); } setDeclData(cursor, static_cast(decl)); if (CK == CXCursor_UnionDecl) decl->setClassType(ClassDeclarationData::Union); if (CK == CXCursor_StructDecl) decl->setClassType(ClassDeclarationData::Struct); if (clang_isCursorDefinition(cursor)) { decl->setDeclarationIsDefinition(true); } if (!s_jsonTestRun) { // don't add this info to the json tests, it invalidates the comment structure auto type = clang_getCursorType(cursor); auto sizeOf = clang_Type_getSizeOf(type); auto alignOf = clang_Type_getAlignOf(type); if (sizeOf >= 0 && alignOf >= 0) { decl->setComment(decl->comment() + i18n("
size: %1 Bytes
" "aligned to: %2 Bytes", sizeOf, alignOf).toUtf8()); } } } template void Visitor::setDeclData(CXCursor cursor, AbstractFunctionDeclaration* decl) const { if (m_update) { decl->clearDefaultParameters(); } // No setDeclData(...) here: AbstractFunctionDeclaration is an interface // TODO: Can we get the default arguments directly from Clang? // also see http://clang-developers.42468.n3.nabble.com/Finding-default-value-for-function-argument-with-clang-c-API-td4036919.html const QVector defaultArgs = ClangUtils::getDefaultArguments(cursor, ClangUtils::MinimumSize); foreach (const QString& defaultArg, defaultArgs) { decl->addDefaultParameter(IndexedString(defaultArg)); } } template void Visitor::setDeclData(CXCursor cursor, ClassFunctionDeclaration* decl) const { setDeclData(cursor, static_cast(decl)); setDeclData(cursor, static_cast(decl)); decl->setAbstract(clang_CXXMethod_isPureVirtual(cursor)); decl->setStatic(clang_CXXMethod_isStatic(cursor)); decl->setVirtual(clang_CXXMethod_isVirtual(cursor)); } template void Visitor::setDeclData(CXCursor cursor, FunctionDeclaration *decl, bool setComment) const { setDeclData(cursor, static_cast(decl)); setDeclData(cursor, static_cast(decl), setComment); } template void Visitor::setDeclData(CXCursor cursor, FunctionDefinition *decl) const { bool setComment = clang_equalCursors(clang_getCanonicalCursor(cursor), cursor); setDeclData(cursor, static_cast(decl), setComment); } template void Visitor::setDeclData(CXCursor cursor, NamespaceAliasDeclaration *decl) const { setDeclData(cursor, static_cast(decl)); clang_visitChildren(cursor, [] (CXCursor cursor, CXCursor /*parent*/, CXClientData data) -> CXChildVisitResult { Q_ASSERT(clang_getCursorKind(cursor) == CXCursor_NamespaceRef); const auto id = QualifiedIdentifier(ClangString(clang_getCursorSpelling(cursor)).toString()); reinterpret_cast(data)->setImportIdentifier(id); return CXChildVisit_Break; }, decl); } //END setDeclData //BEGIN build* template CXChildVisitResult Visitor::buildDeclaration(CXCursor cursor) { auto id = makeId(cursor); if (CK == CXCursor_UnexposedDecl && id.isEmpty()) { // skip unexposed declarations that have no identifier set // this is useful to skip e.g. friend declarations return CXChildVisit_Recurse; } IF_DEBUG(clangDebug() << "id:" << id << "- CK:" << CK << "- DeclType:" << typeid(DeclType).name() << "- hasContext:" << hasContext;) // Code path for class declarations that may be defined "out-of-line", e.g. // "SomeNameSpace::SomeClass {};" QScopedPointer helperContext; if (CursorKindTraits::isClass(CK) || CursorKindTraits::isFunction(CK)) { const auto lexicalParent = clang_getCursorLexicalParent(cursor); const auto semanticParent = clang_getCursorSemanticParent(cursor); const bool isOutOfLine = !clang_equalCursors(lexicalParent, semanticParent); if (isOutOfLine) { const QString scope = ClangUtils::getScope(cursor); auto context = createContext(cursor, QualifiedIdentifier(scope)); helperContext.reset(new CurrentContext(context)); } } // if helperContext is null, this is a no-op PushValue pushCurrent(m_parentContext, helperContext.isNull() ? m_parentContext : helperContext.data()); if (hasContext) { auto context = createContext(cursor, QualifiedIdentifier(id)); createDeclaration(cursor, id, context); CurrentContext newParent(context); PushValue pushCurrent(m_parentContext, &newParent); clang_visitChildren(cursor, &visitCursor, this); return CXChildVisit_Continue; } createDeclaration(cursor, id, nullptr); return CXChildVisit_Recurse; } CXChildVisitResult Visitor::buildParmDecl(CXCursor cursor) { return buildDeclaration::Type, false>(cursor); } CXChildVisitResult Visitor::buildUse(CXCursor cursor) { m_uses[m_parentContext->context].push_back(cursor); return cursor.kind == CXCursor_DeclRefExpr || cursor.kind == CXCursor_MemberRefExpr ? CXChildVisit_Recurse : CXChildVisit_Continue; } CXChildVisitResult Visitor::buildMacroExpansion(CXCursor cursor) { buildUse(cursor); // cache that we encountered a macro expansion at this location unsigned int offset; clang_getSpellingLocation(clang_getCursorLocation(cursor), nullptr, nullptr, nullptr, &offset); m_macroExpansionLocations << offset; return CXChildVisit_Recurse; } CXChildVisitResult Visitor::buildCompoundStatement(CXCursor cursor) { if (m_parentContext->context->type() == DUContext::Function) { auto context = createContext(cursor); CurrentContext newParent(context); PushValue pushCurrent(m_parentContext, &newParent); clang_visitChildren(cursor, &visitCursor, this); return CXChildVisit_Continue; } return CXChildVisit_Recurse; } CXChildVisitResult Visitor::buildCXXBaseSpecifier(CXCursor cursor) { auto currentContext = m_parentContext->context; bool virtualInherited = clang_isVirtualBase(cursor); Declaration::AccessPolicy access = CursorKindTraits::kdevAccessPolicy(clang_getCXXAccessSpecifier(cursor)); auto classDeclCursor = clang_getCursorReferenced(cursor); auto decl = findDeclaration(classDeclCursor); if (!decl) { // this happens for templates with template-dependent base classes e.g. - dunno whether we can/should do more here clangDebug() << "failed to find declaration for base specifier:" << clang_getCursorDisplayName(cursor); return CXChildVisit_Recurse; } DUChainWriteLocker lock; contextImportDecl(currentContext, decl); auto classDecl = dynamic_cast(currentContext->owner()); Q_ASSERT(classDecl); classDecl->addBaseClass({decl->indexedType(), access, virtualInherited}); return CXChildVisit_Recurse; } //END build* DeclarationPointer Visitor::findDeclaration(CXCursor cursor) const { const auto it = m_cursorToDeclarationCache.constFind(cursor); if (it != m_cursorToDeclarationCache.constEnd()) { return *it; } // fallback, and cache result auto decl = ClangHelpers::findDeclaration(cursor, m_includes); m_cursorToDeclarationCache.insert(cursor, decl); return decl; } void Visitor::setIdTypeDecl(CXCursor typeCursor, IdentifiedType* idType) const { DeclarationPointer decl = findDeclaration(typeCursor); DUChainReadLocker lock; if (decl) { idType->setDeclaration(decl.data()); } } AbstractType *Visitor::makeType(CXType type, CXCursor parent) { #define UseKind(TypeKind) case TypeKind: return dispatchType(type, parent) switch (type.kind) { UseKind(CXType_Void); UseKind(CXType_Bool); UseKind(CXType_Short); UseKind(CXType_UShort); UseKind(CXType_Int); UseKind(CXType_UInt); UseKind(CXType_Long); UseKind(CXType_ULong); UseKind(CXType_LongLong); UseKind(CXType_ULongLong); UseKind(CXType_Float); UseKind(CXType_LongDouble); UseKind(CXType_Double); UseKind(CXType_Char_U); UseKind(CXType_Char_S); UseKind(CXType_UChar); UseKind(CXType_SChar); UseKind(CXType_Char16); UseKind(CXType_Char32); UseKind(CXType_Pointer); UseKind(CXType_MemberPointer); UseKind(CXType_ObjCObjectPointer); UseKind(CXType_ConstantArray); UseKind(CXType_VariableArray); UseKind(CXType_IncompleteArray); UseKind(CXType_DependentSizedArray); UseKind(CXType_LValueReference); UseKind(CXType_RValueReference); UseKind(CXType_FunctionProto); UseKind(CXType_Record); UseKind(CXType_Enum); UseKind(CXType_Typedef); UseKind(CXType_Int128); UseKind(CXType_UInt128); UseKind(CXType_Vector); UseKind(CXType_Unexposed); UseKind(CXType_WChar); UseKind(CXType_ObjCInterface); UseKind(CXType_ObjCId); UseKind(CXType_ObjCClass); UseKind(CXType_ObjCSel); UseKind(CXType_NullPtr); +#if CINDEX_VERSION_MINOR >= 31 + UseKind(CXType_Auto); +#endif case CXType_Invalid: return nullptr; default: qCWarning(KDEV_CLANG) << "Unhandled type:" << type.kind << clang_getTypeSpelling(type); return nullptr; } } RangeInRevision rangeInRevisionForUse(CXCursor cursor, CXCursorKind referencedCursorKind, CXSourceRange useRange, const QSet& macroExpansionLocations) { auto range = ClangRange(useRange).toRangeInRevision(); //TODO: Fix in clang, happens for operator<<, operator<, probably more if (clang_Range_isNull(useRange)) { useRange = clang_getCursorExtent(cursor); range = ClangRange(useRange).toRangeInRevision(); } if (referencedCursorKind == CXCursor_ConversionFunction) { range.end = range.start; range.start.column--; } // For uses inside macro expansions, create an empty use range at the spelling location // the empty range is required in order to not "overlap" the macro expansion range // and to allow proper navigation for the macro expansion // also see JSON test 'macros.cpp' if (clang_getCursorKind(cursor) != CXCursor_MacroExpansion) { unsigned int expansionLocOffset; const auto spellingLocation = clang_getRangeStart(useRange); clang_getExpansionLocation(spellingLocation, nullptr, nullptr, nullptr, &expansionLocOffset); if (macroExpansionLocations.contains(expansionLocOffset)) { unsigned int spellingLocOffset; clang_getSpellingLocation(spellingLocation, nullptr, nullptr, nullptr, &spellingLocOffset); if (spellingLocOffset == expansionLocOffset) { range.end = range.start; } } } else { // Workaround for wrong use range returned by clang for macro expansions const auto contents = ClangUtils::getRawContents(clang_Cursor_getTranslationUnit(cursor), useRange); const int firstOpeningParen = contents.indexOf('('); if (firstOpeningParen != -1) { range.end.column = range.start.column + firstOpeningParen; range.end.line = range.start.line; } } return range; } Visitor::Visitor(CXTranslationUnit tu, CXFile file, const IncludeFileContexts& includes, const bool update) : m_file(file) , m_includes(includes) , m_parentContext(nullptr) , m_update(update) { CXCursor tuCursor = clang_getTranslationUnitCursor(tu); CurrentContext parent(includes[file]); m_parentContext = &parent; clang_visitChildren(tuCursor, &visitCursor, this); TopDUContext *top = m_parentContext->context->topContext(); if (m_update) { DUChainWriteLocker lock; top->deleteUsesRecursively(); } for (const auto &contextUses : m_uses) { for (const auto &cursor : contextUses.second) { auto referenced = referencedCursor(cursor); if (clang_Cursor_isNull(referenced)) { continue; } // first, try the canonical referenced cursor // this is important to get the correct function declaration e.g. auto canonicalReferenced = clang_getCanonicalCursor(referenced); auto used = findDeclaration(canonicalReferenced); if (!used) { // if the above failed, try the non-canonicalized version as a fallback // this is required for friend declarations that occur before // the real declaration. there, the canonical cursor points to // the friend declaration which is not what we are looking for used = findDeclaration(referenced); } if (!used) { // as a last resort, try to resolve the forward declaration DUChainReadLocker lock; DeclarationPointer decl = ClangHelpers::findForwardDeclaration(clang_getCursorType(referenced), contextUses.first, referenced); used = decl; if (!used) { continue; } } #if CINDEX_VERSION_MINOR >= 29 if (clang_Cursor_getNumTemplateArguments(referenced) >= 0) { // Ideally, we don't need this, but for function templates clang_getCanonicalCursor returns a function definition // See also the testUsesCreatedForDeclarations test DUChainReadLocker lock; used = DUChainUtils::declarationForDefinition(used.data()); } #endif const auto useRange = clang_getCursorReferenceNameRange(cursor, 0, 0); const auto range = rangeInRevisionForUse(cursor, referenced.kind, useRange, m_macroExpansionLocations); DUChainWriteLocker lock; auto usedIndex = top->indexForUsedDeclaration(used.data()); contextUses.first->createUse(usedIndex, range); } } } //END Visitor CXChildVisitResult visitCursor(CXCursor cursor, CXCursor parent, CXClientData data) { Visitor *visitor = static_cast(data); const auto kind = clang_getCursorKind(cursor); auto location = clang_getCursorLocation(cursor); CXFile file; clang_getFileLocation(location, &file, nullptr, nullptr, nullptr); // don't skip MemberRefExpr with invalid location, see also: // http://lists.cs.uiuc.edu/pipermail/cfe-dev/2015-May/043114.html if (!ClangUtils::isFileEqual(file, visitor->m_file) && (file || kind != CXCursor_MemberRefExpr)) { return CXChildVisit_Continue; } #define UseCursorKind(CursorKind, ...) case CursorKind: return visitor->dispatchCursor(__VA_ARGS__); switch (kind) { UseCursorKind(CXCursor_UnexposedDecl, cursor, parent); UseCursorKind(CXCursor_StructDecl, cursor, parent); UseCursorKind(CXCursor_UnionDecl, cursor, parent); UseCursorKind(CXCursor_ClassDecl, cursor, parent); UseCursorKind(CXCursor_EnumDecl, cursor, parent); UseCursorKind(CXCursor_FieldDecl, cursor, parent); UseCursorKind(CXCursor_EnumConstantDecl, cursor, parent); UseCursorKind(CXCursor_FunctionDecl, cursor, parent); UseCursorKind(CXCursor_VarDecl, cursor, parent); UseCursorKind(CXCursor_TypeAliasDecl, cursor, parent); UseCursorKind(CXCursor_TypedefDecl, cursor, parent); UseCursorKind(CXCursor_CXXMethod, cursor, parent); UseCursorKind(CXCursor_Namespace, cursor, parent); UseCursorKind(CXCursor_NamespaceAlias, cursor, parent); UseCursorKind(CXCursor_Constructor, cursor, parent); UseCursorKind(CXCursor_Destructor, cursor, parent); UseCursorKind(CXCursor_ConversionFunction, cursor, parent); UseCursorKind(CXCursor_TemplateTypeParameter, cursor, parent); UseCursorKind(CXCursor_NonTypeTemplateParameter, cursor, parent); UseCursorKind(CXCursor_TemplateTemplateParameter, cursor, parent); UseCursorKind(CXCursor_FunctionTemplate, cursor, parent); UseCursorKind(CXCursor_ClassTemplate, cursor, parent); UseCursorKind(CXCursor_ClassTemplatePartialSpecialization, cursor, parent); UseCursorKind(CXCursor_ObjCInterfaceDecl, cursor, parent); UseCursorKind(CXCursor_ObjCCategoryDecl, cursor, parent); UseCursorKind(CXCursor_ObjCProtocolDecl, cursor, parent); UseCursorKind(CXCursor_ObjCPropertyDecl, cursor, parent); UseCursorKind(CXCursor_ObjCIvarDecl, cursor, parent); UseCursorKind(CXCursor_ObjCInstanceMethodDecl, cursor, parent); UseCursorKind(CXCursor_ObjCClassMethodDecl, cursor, parent); UseCursorKind(CXCursor_ObjCImplementationDecl, cursor, parent); UseCursorKind(CXCursor_ObjCCategoryImplDecl, cursor, parent); UseCursorKind(CXCursor_MacroDefinition, cursor, parent); UseCursorKind(CXCursor_LabelStmt, cursor, parent); case CXCursor_TypeRef: case CXCursor_TemplateRef: case CXCursor_NamespaceRef: case CXCursor_MemberRef: case CXCursor_LabelRef: case CXCursor_OverloadedDeclRef: case CXCursor_VariableRef: case CXCursor_DeclRefExpr: case CXCursor_MemberRefExpr: case CXCursor_ObjCClassRef: return visitor->buildUse(cursor); case CXCursor_MacroExpansion: return visitor->buildMacroExpansion(cursor); case CXCursor_CompoundStmt: return visitor->buildCompoundStatement(cursor); case CXCursor_CXXBaseSpecifier: return visitor->buildCXXBaseSpecifier(cursor); case CXCursor_ParmDecl: return visitor->buildParmDecl(cursor); default: return CXChildVisit_Recurse; } } } namespace Builder { void visit(CXTranslationUnit tu, CXFile file, const IncludeFileContexts& includes, const bool update) { Visitor visitor(tu, file, includes, update); } void enableJSONTestRun() { s_jsonTestRun = true; } } \ No newline at end of file diff --git a/languages/clang/duchain/gcc_compat.h b/languages/clang/duchain/gcc_compat.h new file mode 100644 index 0000000000..6bd10659ad --- /dev/null +++ b/languages/clang/duchain/gcc_compat.h @@ -0,0 +1,1133 @@ +/* + This file is part of KDevelop + + Copyright 2015 Milian Wolff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// this file is automatically included by our clang backend for C/C++ projects +// to increase compatibility with GCC by adding some __m64rinsics etc. +// this is required to remove parse errors in GCC headers when using a +// GCC toolchain + +inline namespace KDevelopClangGccCompat { + +typedef int __m64 __attribute__ ((__vector_size__ (8), __may_alias__)); + +typedef float __m128 __attribute__ ((__vector_size__ (16), __may_alias__)); +typedef long long __m128i __attribute__ ((__vector_size__ (16), __may_alias__)); +typedef double __m128d __attribute__ ((__vector_size__ (16), __may_alias__)); + +typedef float __m256 __attribute__ ((__vector_size__ (32), __may_alias__)); +typedef long long __m256i __attribute__ ((__vector_size__ (32), __may_alias__)); +typedef double __m256d __attribute__ ((__vector_size__ (32), __may_alias__)); + +typedef float __m512 __attribute__ ((__vector_size__ (64), __may_alias__)); +typedef long long __m512i __attribute__ ((__vector_size__ (64), __may_alias__)); +typedef double __m512d __attribute__ ((__vector_size__ (64), __may_alias__)); + +typedef unsigned char __mmask8; +typedef unsigned short __mmask16; +typedef unsigned int __mmask32; +typedef unsigned long long __mmask64; + +// +// the below was generated using the following bash magic: +// +// tests/test_duchain-clang testGccCompatibility | grep -Po "\b__builtin_[a-z_A-Z0-9]*\b" | sort | uniq > /tmp/builtins.txt +// then, in your GCC include path, e.g. /usr/lib/gcc/x86_64-unknown-linux-gnu/5.2.0/include: +// (while read f; do r=$(ack "\b$f\b" -B 5 -1 | grep "extern __inline" | awk '{printf "%s",$3; if ($4 !~ /__attrib/) printf " %s",$4;}'); echo "$r $f(...);"; done) < /tmp/builtins.txt +// +// these steps may need to be repeated multiple times +// + +__m128d __builtin_ia32_addsd(...); +__m128 __builtin_ia32_addss(...); +__m128d __builtin_ia32_andnpd(...); +__m128 __builtin_ia32_andnps(...); +__m128d __builtin_ia32_andpd(...); +__m128 __builtin_ia32_andps(...); +int __builtin_ia32_bsrdi(...); +int __builtin_ia32_bsrsi(...); +__m128d __builtin_ia32_cmpgepd(...); +__m128 __builtin_ia32_cmpgeps(...); +__m128d __builtin_ia32_cmpgtpd(...); +__m128 __builtin_ia32_cmpgtps(...); +__m128 __builtin_ia32_cmpleps(...); +__m128 __builtin_ia32_cmpltps(...); +__m128d __builtin_ia32_cmpngepd(...); +__m128 __builtin_ia32_cmpngeps(...); +__m128d __builtin_ia32_cmpngtpd(...); +__m128 __builtin_ia32_cmpngtps(...); +__m128 __builtin_ia32_cmpnleps(...); +__m128 __builtin_ia32_cmpnltps(...); +__m128 __builtin_ia32_cvtpi2ps(...); +__m128 __builtin_ia32_cvtsd2ss(...); +__m128d __builtin_ia32_cvtsi2sd(...); +__m128 __builtin_ia32_cvtsi2ss(...); +__m128d __builtin_ia32_cvtsi642sd(...); +__m128 __builtin_ia32_cvtsi642ss(...); +__m128d __builtin_ia32_cvtss2sd(...); +int __builtin_ia32_cvtss2si(...); +long long __builtin_ia32_cvtss2si64(...); +int __builtin_ia32_cvttsd2si(...); +long long __builtin_ia32_cvttsd2si64(...); +int __builtin_ia32_cvttss2si(...); +long long __builtin_ia32_cvttss2si64(...); +__m128d __builtin_ia32_divsd(...); +__m128 __builtin_ia32_divss(...); +__m128 __builtin_ia32_haddps(...); +__m128i __builtin_ia32_loaddqu(...); +__m128d __builtin_ia32_loadhpd(...); +__m128 __builtin_ia32_loadhps(...); +__m128d __builtin_ia32_loadlpd(...); +__m128 __builtin_ia32_loadlps(...); +__m128d __builtin_ia32_loadupd(...); +__m128 __builtin_ia32_loadups(...); +__m128 __builtin_ia32_minss(...); +__m128 __builtin_ia32_movhlps(...); +__m128 __builtin_ia32_movlhps(...); +void __builtin_ia32_movntps(...); +__m128i __builtin_ia32_movq128(...); +__m128d __builtin_ia32_movsd(...); +__m128 __builtin_ia32_movss(...); +__m128d __builtin_ia32_mulsd(...); +__m128 __builtin_ia32_mulss(...); +__m128d __builtin_ia32_orpd(...); +__m128 __builtin_ia32_orps(...); +void __builtin_ia32_pause(...); +__m64 __builtin_ia32_por(...); +__m128i __builtin_ia32_punpckhbw128(...); +__m128i __builtin_ia32_punpckhwd128(...); +unsigned long __builtin_ia32_readeflags_u64(...); +unsigned short __builtin_ia32_rolhi(...); +unsigned char __builtin_ia32_rolqi(...); +unsigned short __builtin_ia32_rorhi(...); +unsigned char __builtin_ia32_rorqi(...); +__m128d __builtin_ia32_shufpd(...); +__m128 __builtin_ia32_shufps(...); +__m128d __builtin_ia32_subsd(...); +__m128 __builtin_ia32_subss(...); +__m128d __builtin_ia32_unpckhpd(...); +__m128 __builtin_ia32_unpckhps(...); +__m128d __builtin_ia32_unpcklpd(...); +__m128 __builtin_ia32_unpcklps(...); +int __builtin_ia32_vec_ext_v4si(...); +void __builtin_ia32_writeeflags_u64(...); +__m128d __builtin_ia32_xorpd(...); +__m128 __builtin_ia32_xorps(...); +__m256i __builtin_ia32_andnotsi256(...); +__m256d __builtin_ia32_andnpd256(...); +__m256 __builtin_ia32_andnps256(...); +__m256d __builtin_ia32_andpd256(...); +__m256 __builtin_ia32_andps256(...); +__m512d __builtin_ia32_broadcastsd512(...); +__m512 __builtin_ia32_broadcastss512(...); +__m256d __builtin_ia32_haddpd256(...); +__m256 __builtin_ia32_haddps256(...); +__m256i __builtin_ia32_lddqu256(...); +__m512d __builtin_ia32_loadapd512_mask(...); +__m256i __builtin_ia32_loaddqu256(...); +__m256d __builtin_ia32_loadupd256(...); +__m256 __builtin_ia32_loadups256(...); +__m512d __builtin_ia32_movapd512_mask(...); +__m512 __builtin_ia32_movaps512_mask(...); +__m256d __builtin_ia32_movddup256(...); +__m512i __builtin_ia32_movdqa32_512_mask(...); +__m512i __builtin_ia32_movdqa32load512_mask(...); +__m512i __builtin_ia32_movdqa64_512_mask(...); +__m512i __builtin_ia32_movdqa64load512_mask(...); +void __builtin_ia32_movdqa64store512_mask(...); +__m128i __builtin_ia32_movntdqa(...); +__m128 __builtin_ia32_movshdup(...); +__m256 __builtin_ia32_movshdup256(...); +__m128 __builtin_ia32_movsldup(...); +__m256 __builtin_ia32_movsldup256(...); +__m256d __builtin_ia32_orpd256(...); +__m256 __builtin_ia32_orps256(...); +__m256i __builtin_ia32_pabsd256(...); +__m128i __builtin_ia32_pandn128(...); +__m256i __builtin_ia32_pbroadcastd256(...); +__m256d __builtin_ia32_pd256_pd(...); +__m128d __builtin_ia32_pd_pd256(...); +__m256 __builtin_ia32_ps256_ps(...); +__m128 __builtin_ia32_ps_ps256(...); +__m64 __builtin_ia32_punpckhbw(...); +__m256i __builtin_ia32_punpckhbw256(...); +__m64 __builtin_ia32_punpckhdq(...); +__m128i __builtin_ia32_punpckhdq128(...); +__m256i __builtin_ia32_punpckhdq256(...); +__m128i __builtin_ia32_punpckhqdq128(...); +__m256i __builtin_ia32_punpckhqdq256(...); +__m64 __builtin_ia32_punpckhwd(...); +__m256i __builtin_ia32_punpckhwd256(...); +__m64 __builtin_ia32_punpcklbw(...); +__m128i __builtin_ia32_punpcklbw128(...); +__m256i __builtin_ia32_punpcklbw256(...); +__m64 __builtin_ia32_punpckldq(...); +__m128i __builtin_ia32_punpckldq128(...); +__m256i __builtin_ia32_punpckldq256(...); +__m128i __builtin_ia32_punpcklqdq128(...); +__m256i __builtin_ia32_punpcklqdq256(...); +__m64 __builtin_ia32_punpcklwd(...); +__m128i __builtin_ia32_punpcklwd128(...); +__m256i __builtin_ia32_punpcklwd256(...); +__m256 __builtin_ia32_rcpps256(...); +__m256i __builtin_ia32_si256_si(...); +__m128i __builtin_ia32_si_si256(...); +__m256d __builtin_ia32_unpckhpd256(...); +__m256 __builtin_ia32_unpckhps256(...); +__m256d __builtin_ia32_unpcklpd256(...); +__m256 __builtin_ia32_unpcklps256(...); +__m256d __builtin_ia32_vbroadcastsd256(...); +__m256i __builtin_ia32_vbroadcastsi256(...); +__m128 __builtin_ia32_vbroadcastss(...); +__m256 __builtin_ia32_vbroadcastss256(...); +__m128 __builtin_ia32_vbroadcastss_ps(...); +__m256d __builtin_ia32_xorpd256(...); +__m256 __builtin_ia32_xorps256(...); +void __builtin_ia32_movdqa32store512_mask(...); +__m512i __builtin_ia32_pmovsxbd512_mask(...); +__m512i __builtin_ia32_pmovsxbq512_mask(...); +__m512i __builtin_ia32_pmovsxdq512_mask(...); +__m512i __builtin_ia32_pmovsxwd512_mask(...); +__m512i __builtin_ia32_pmovsxwq512_mask(...); +__m512i __builtin_ia32_pmovzxbd512_mask(...); +__m512i __builtin_ia32_pslld512_mask(...); +__m512i __builtin_ia32_psllq512_mask(...); +__m512i __builtin_ia32_psllv16si_mask(...); +__m512i __builtin_ia32_psllv8di_mask(...); +__m512i __builtin_ia32_psrad512_mask(...); +__m512i __builtin_ia32_psraq512_mask(...); +__m512i __builtin_ia32_psrav16si_mask(...); +__m512i __builtin_ia32_psrav8di_mask(...); +__m512i __builtin_ia32_psrld512_mask(...); +__m512i __builtin_ia32_psrlq512_mask(...); +__m512i __builtin_ia32_psrlv16si_mask(...); +__m512i __builtin_ia32_psrlv8di_mask(...); +__m128d __builtin_ia32_rcp14sd(...); +__m128 __builtin_ia32_rcp14ss(...); +__m128d __builtin_ia32_rsqrt14sd(...); +__m128 __builtin_ia32_rsqrt14ss(...); +__m512 __builtin_ia32_broadcastf32x4_512(...); +__m512d __builtin_ia32_broadcastf64x4_512(...); +__m512i __builtin_ia32_broadcasti32x4_512(...); +__m512i __builtin_ia32_broadcasti64x4_512(...); +__m128d __builtin_ia32_cvtusi2sd32(...); +__m512i __builtin_ia32_pbroadcastd512(...); +__m512i __builtin_ia32_pbroadcastq512(...); +__m128i __builtin_ia32_pmovdb512_mask(...); +void __builtin_ia32_pmovdb512mem_mask(...); +__m256i __builtin_ia32_pmovdw512_mask(...); +void __builtin_ia32_pmovdw512mem_mask(...); +__m256i __builtin_ia32_pmovqd512_mask(...); +void __builtin_ia32_pmovqd512mem_mask(...); +__m128i __builtin_ia32_pmovqw512_mask(...); +void __builtin_ia32_pmovqw512mem_mask(...); +__m128i __builtin_ia32_pmovsdb512_mask(...); +void __builtin_ia32_pmovsdb512mem_mask(...); +__m256i __builtin_ia32_pmovsdw512_mask(...); +void __builtin_ia32_pmovsdw512mem_mask(...); +__m256i __builtin_ia32_pmovsqd512_mask(...); +void __builtin_ia32_pmovsqd512mem_mask(...); +__m128i __builtin_ia32_pmovusdb512_mask(...); +void __builtin_ia32_pmovusdb512mem_mask(...); +__m256i __builtin_ia32_pmovusdw512_mask(...); +void __builtin_ia32_pmovusdw512mem_mask(...); +__m256i __builtin_ia32_pmovusqd512_mask(...); +void __builtin_ia32_pmovusqd512mem_mask(...); +__m512i __builtin_ia32_pmovzxbq512_mask(...); +__m512i __builtin_ia32_pmovzxdq512_mask(...); +__m512i __builtin_ia32_pmovzxwd512_mask(...); +__m512i __builtin_ia32_pmovzxwq512_mask(...); +__m512i __builtin_ia32_prolvd512_mask(...); +__m512i __builtin_ia32_prolvq512_mask(...); +__m512i __builtin_ia32_prorvd512_mask(...); +__m512i __builtin_ia32_prorvq512_mask(...); +__m512d __builtin_ia32_compressdf512_mask(...); +__m512d __builtin_ia32_movddup512_mask(...); +void __builtin_ia32_movntdq512(...); +__m512i __builtin_ia32_movntdqa512(...); +void __builtin_ia32_movntpd512(...); +void __builtin_ia32_movntps512(...); +__m512 __builtin_ia32_movshdup512_mask(...); +__m512 __builtin_ia32_movsldup512_mask(...); +__mmask64 __builtin_ia32_pcmpeqb512_mask(...); +__m512d __builtin_ia32_permvardf512_mask(...); +__m512i __builtin_ia32_permvardi512_mask(...); +__m512 __builtin_ia32_permvarsf512_mask(...); +__m512i __builtin_ia32_permvarsi512_mask(...); +__m128i __builtin_ia32_pmovqb512_mask(...); +void __builtin_ia32_pmovqb512mem_mask(...); +__m128i __builtin_ia32_pmovsqb512_mask(...); +void __builtin_ia32_pmovsqb512mem_mask(...); +__m128i __builtin_ia32_pmovsqw512_mask(...); +void __builtin_ia32_pmovsqw512mem_mask(...); +__m128i __builtin_ia32_pmovusqb512_mask(...); +void __builtin_ia32_pmovusqb512mem_mask(...); +__m128i __builtin_ia32_pmovusqw512_mask(...); +void __builtin_ia32_pmovusqw512mem_mask(...); +__mmask16 __builtin_ia32_ptestnmd512(...); +__mmask8 __builtin_ia32_ptestnmq512(...); +__m512i __builtin_ia32_punpckhdq512_mask(...); +__m512i __builtin_ia32_punpckhqdq512_mask(...); +__m512i __builtin_ia32_punpckldq512_mask(...); +__m512i __builtin_ia32_punpcklqdq512_mask(...); +__m512d __builtin_ia32_unpckhpd512_mask(...); +__m512 __builtin_ia32_unpckhps512_mask(...); +__m512d __builtin_ia32_unpcklpd512_mask(...); +__m512i __builtin_ia32_vpermi2vard512_mask(...); +__m512d __builtin_ia32_vpermi2varpd512_mask(...); +__m512 __builtin_ia32_vpermi2varps512_mask(...); +__m512i __builtin_ia32_vpermi2varq512_mask(...); +__m512d __builtin_ia32_vpermilvarpd512_mask(...); +__m512 __builtin_ia32_vpermilvarps512_mask(...); +__m512i __builtin_ia32_vpermt2vard512_mask(...); +__m512i __builtin_ia32_vpermt2vard512_maskz(...); +__m512d __builtin_ia32_vpermt2varpd512_mask(...); +__m512d __builtin_ia32_vpermt2varpd512_maskz(...); +__m512 __builtin_ia32_vpermt2varps512_mask(...); +__m512 __builtin_ia32_vpermt2varps512_maskz(...); +__m512i __builtin_ia32_vpermt2varq512_mask(...); +__m512i __builtin_ia32_vpermt2varq512_maskz(...); +__m512d __builtin_ia32_addpd512_mask(...); +__m512 __builtin_ia32_addps512_mask(...); +__m512i __builtin_ia32_broadcastmb512(...); +__m512i __builtin_ia32_compressdi512_mask(...); +__m512 __builtin_ia32_compresssf512_mask(...); +__m512i __builtin_ia32_compresssi512_mask(...); +void __builtin_ia32_compressstoredf512_mask(...); +void __builtin_ia32_compressstoredi512_mask(...); +void __builtin_ia32_compressstoresf512_mask(...); +void __builtin_ia32_compressstoresi512_mask(...); +__m512d __builtin_ia32_cvtps2pd512_mask(...); +__m128d __builtin_ia32_cvtusi2sd64(...); +__m128 __builtin_ia32_cvtusi2ss32(...); +__m128 __builtin_ia32_cvtusi2ss64(...); +__m512d __builtin_ia32_divpd512_mask(...); +__m512 __builtin_ia32_divps512_mask(...); +__m512d __builtin_ia32_expanddf512_mask(...); +__m512d __builtin_ia32_expanddf512_maskz(...); +__m512i __builtin_ia32_expanddi512_mask(...); +__m512i __builtin_ia32_expanddi512_maskz(...); +__m512d __builtin_ia32_expandloaddf512_mask(...); +__m512d __builtin_ia32_expandloaddf512_maskz(...); +__m512i __builtin_ia32_expandloaddi512_mask(...); +__m512i __builtin_ia32_expandloaddi512_maskz(...); +__m512 __builtin_ia32_expandloadsf512_mask(...); +__m512 __builtin_ia32_expandloadsf512_maskz(...); +__m512i __builtin_ia32_expandloadsi512_mask(...); +__m512i __builtin_ia32_expandloadsi512_maskz(...); +__m512 __builtin_ia32_expandsf512_mask(...); +__m512 __builtin_ia32_expandsf512_maskz(...); +__m512i __builtin_ia32_expandsi512_mask(...); +__m512i __builtin_ia32_expandsi512_maskz(...); +__m128i __builtin_ia32_extracti32x4_mask(...); +__mmask16 __builtin_ia32_kandhi(...); +__mmask16 __builtin_ia32_kandnhi(...); +__mmask16 __builtin_ia32_kmov16(...); +__mmask16 __builtin_ia32_korhi(...); +int __builtin_ia32_kortestchi(...); +int __builtin_ia32_kortestzhi(...); +__mmask16 __builtin_ia32_kunpckhi(...); +__mmask16 __builtin_ia32_kxnorhi(...); +__mmask16 __builtin_ia32_kxorhi(...); +__m512d __builtin_ia32_mulpd512_mask(...); +__m512 __builtin_ia32_mulps512_mask(...); +__m512i __builtin_ia32_pabsd512_mask(...); +__m64 __builtin_ia32_pandn(...); +__mmask16 __builtin_ia32_pcmpeqd512_mask(...); +__m512d __builtin_ia32_pd512_256pd(...); +__m512d __builtin_ia32_pd512_pd(...); +__m512 __builtin_ia32_ps512_256ps(...); +__m512 __builtin_ia32_ps512_ps(...); +__m64 __builtin_ia32_pxor(...); +__m512d __builtin_ia32_scalefpd512_mask(...); +__m512 __builtin_ia32_scalefps512_mask(...); +__m128d __builtin_ia32_scalefsd_round(...); +__m128 __builtin_ia32_scalefss_round(...); +__m512i __builtin_ia32_si512_256si(...); +__m512i __builtin_ia32_si512_si(...); +__m512d __builtin_ia32_subpd512_mask(...); +__m512 __builtin_ia32_subps512_mask(...); +__m512 __builtin_ia32_unpcklps512_mask(...); +unsigned __builtin_ia32_vcvtsd2usi32(...); +unsigned long __builtin_ia32_vcvtsd2usi64(...); +unsigned __builtin_ia32_vcvtss2usi32(...); +unsigned long __builtin_ia32_vcvtss2usi64(...); +int __builtin_ia32_vcvttsd2si32(...); +long long __builtin_ia32_vcvttsd2si64(...); +unsigned __builtin_ia32_vcvttsd2usi32(...); +unsigned long __builtin_ia32_vcvttsd2usi64(...); +int __builtin_ia32_vcvttss2si32(...); +long long __builtin_ia32_vcvttss2si64(...); +unsigned __builtin_ia32_vcvttss2usi32(...); +unsigned long __builtin_ia32_vcvttss2usi64(...); +__m128d __builtin_ia32_addpd128_mask(...); +__m256d __builtin_ia32_addpd256_mask(...); +__m128 __builtin_ia32_addps128_mask(...); +__m256 __builtin_ia32_addps256_mask(...); +__m128d __builtin_ia32_andpd128_mask(...); +__m256d __builtin_ia32_andpd256_mask(...); +__m128 __builtin_ia32_andps128_mask(...); +__m256 __builtin_ia32_andps256_mask(...); +__m512i __builtin_ia32_broadcastmw512(...); +__m128d __builtin_ia32_loadapd128_mask(...); +__m256d __builtin_ia32_loadapd256_mask(...); +__m128 __builtin_ia32_loadaps128_mask(...); +__m256 __builtin_ia32_loadaps256_mask(...); +__m512 __builtin_ia32_loadaps512_mask(...); +__m128i __builtin_ia32_loaddqudi128_mask(...); +__m256i __builtin_ia32_loaddqudi256_mask(...); +__m128i __builtin_ia32_loaddqusi128_mask(...); +__m256i __builtin_ia32_loaddqusi256_mask(...); +__m128d __builtin_ia32_loadupd128_mask(...); +__m256d __builtin_ia32_loadupd256_mask(...); +__m128 __builtin_ia32_loadups128_mask(...); +__m256 __builtin_ia32_loadups256_mask(...); +__m128d __builtin_ia32_movapd128_mask(...); +__m256d __builtin_ia32_movapd256_mask(...); +__m128 __builtin_ia32_movaps128_mask(...); +__m256 __builtin_ia32_movaps256_mask(...); +__m128i __builtin_ia32_movdqa32_128_mask(...); +__m256i __builtin_ia32_movdqa32_256_mask(...); +__m128i __builtin_ia32_movdqa32load128_mask(...); +__m256i __builtin_ia32_movdqa32load256_mask(...); +void __builtin_ia32_movdqa32store128_mask(...); +void __builtin_ia32_movdqa32store256_mask(...); +__m128i __builtin_ia32_movdqa64_128_mask(...); +__m256i __builtin_ia32_movdqa64_256_mask(...); +__m128i __builtin_ia32_movdqa64load128_mask(...); +__m256i __builtin_ia32_movdqa64load256_mask(...); +void __builtin_ia32_movdqa64store128_mask(...); +void __builtin_ia32_movdqa64store256_mask(...); +__m128i __builtin_ia32_pabsd128_mask(...); +__m256i __builtin_ia32_pabsd256_mask(...); +__m128i __builtin_ia32_pbroadcastw128(...); +__m128i __builtin_ia32_psubd128_mask(...); +void __builtin_ia32_storeapd128_mask(...); +void __builtin_ia32_storeapd256_mask(...); +void __builtin_ia32_storeapd512_mask(...); +void __builtin_ia32_storeaps128_mask(...); +void __builtin_ia32_storeaps256_mask(...); +void __builtin_ia32_storeaps512_mask(...); +void __builtin_ia32_storedqudi128_mask(...); +void __builtin_ia32_storedqudi256_mask(...); +void __builtin_ia32_storedqusi128_mask(...); +void __builtin_ia32_storedqusi256_mask(...); +void __builtin_ia32_storeupd128_mask(...); +void __builtin_ia32_storeupd256_mask(...); +void __builtin_ia32_storeups128_mask(...); +void __builtin_ia32_storeups256_mask(...); +__m128d __builtin_ia32_subpd128_mask(...); +__m256d __builtin_ia32_subpd256_mask(...); +__m128 __builtin_ia32_subps128_mask(...); +__m256 __builtin_ia32_subps256_mask(...); +__m128d __builtin_ia32_cvtdq2pd128_mask(...); +__m256d __builtin_ia32_cvtdq2pd256_mask(...); +__m512d __builtin_ia32_cvtdq2pd512_mask(...); +__m128 __builtin_ia32_cvtdq2ps128_mask(...); +__m256 __builtin_ia32_cvtdq2ps256_mask(...); +__m128i __builtin_ia32_cvtpd2dq128_mask(...); +__m128i __builtin_ia32_cvtpd2dq256_mask(...); +__m128i __builtin_ia32_cvtpd2udq128_mask(...); +__m128i __builtin_ia32_cvtpd2udq256_mask(...); +__m128d __builtin_ia32_cvtps2pd128_mask(...); +__m256d __builtin_ia32_cvtps2pd256_mask(...); +__m128i __builtin_ia32_cvttpd2dq128_mask(...); +__m128i __builtin_ia32_cvttpd2dq256_mask(...); +__m128i __builtin_ia32_cvttpd2udq128_mask(...); +__m128i __builtin_ia32_cvttpd2udq256_mask(...); +__m128i __builtin_ia32_cvttps2dq128_mask(...); +__m256i __builtin_ia32_cvttps2dq256_mask(...); +__m128i __builtin_ia32_cvttps2udq128_mask(...); +__m256i __builtin_ia32_cvttps2udq256_mask(...); +__m128d __builtin_ia32_cvtudq2pd128_mask(...); +__m256d __builtin_ia32_cvtudq2pd256_mask(...); +__m512d __builtin_ia32_cvtudq2pd512_mask(...); +__m128 __builtin_ia32_cvtudq2ps128_mask(...); +__m256 __builtin_ia32_cvtudq2ps256_mask(...); +__m128i __builtin_ia32_pabsq128_mask(...); +__m256i __builtin_ia32_pabsq256_mask(...); +__m128i __builtin_ia32_pmovdb128_mask(...); +void __builtin_ia32_pmovdb128mem_mask(...); +__m128i __builtin_ia32_pmovdb256_mask(...); +void __builtin_ia32_pmovdb256mem_mask(...); +__m128i __builtin_ia32_pmovsdb128_mask(...); +void __builtin_ia32_pmovsdb128mem_mask(...); +__m128i __builtin_ia32_pmovsdb256_mask(...); +void __builtin_ia32_pmovsdb256mem_mask(...); +__m128i __builtin_ia32_pmovusdb128_mask(...); +void __builtin_ia32_pmovusdb128mem_mask(...); +__m128i __builtin_ia32_pabsw128_mask(...); +__m256i __builtin_ia32_pabsw256_mask(...); +__mmask16 __builtin_ia32_pcmpeqb128_mask(...); +__mmask32 __builtin_ia32_pcmpeqb256_mask(...); +__m128i __builtin_ia32_pmovdw128_mask(...); +void __builtin_ia32_pmovdw128mem_mask(...); +__m128i __builtin_ia32_pmovdw256_mask(...); +void __builtin_ia32_pmovdw256mem_mask(...); +__m128i __builtin_ia32_pmovqb128_mask(...); +void __builtin_ia32_pmovqb128mem_mask(...); +__m128i __builtin_ia32_pmovqb256_mask(...); +void __builtin_ia32_pmovqb256mem_mask(...); +__m128i __builtin_ia32_pmovqd128_mask(...); +void __builtin_ia32_pmovqd128mem_mask(...); +__m128i __builtin_ia32_pmovqd256_mask(...); +void __builtin_ia32_pmovqd256mem_mask(...); +__m128i __builtin_ia32_pmovqw128_mask(...); +void __builtin_ia32_pmovqw128mem_mask(...); +__m128i __builtin_ia32_pmovqw256_mask(...); +void __builtin_ia32_pmovqw256mem_mask(...); +__m128i __builtin_ia32_pmovsdw128_mask(...); +void __builtin_ia32_pmovsdw128mem_mask(...); +__m128i __builtin_ia32_pmovsdw256_mask(...); +void __builtin_ia32_pmovsdw256mem_mask(...); +__m128i __builtin_ia32_pmovsqb128_mask(...); +void __builtin_ia32_pmovsqb128mem_mask(...); +__m128i __builtin_ia32_pmovsqb256_mask(...); +void __builtin_ia32_pmovsqb256mem_mask(...); +__m128i __builtin_ia32_pmovsqd128_mask(...); +__m128i __builtin_ia32_pmovsqw128_mask(...); +void __builtin_ia32_pmovsqw128mem_mask(...); +__m128i __builtin_ia32_pmovsqw256_mask(...); +void __builtin_ia32_pmovsqw256mem_mask(...); +__m128i __builtin_ia32_pmovusdb256_mask(...); +void __builtin_ia32_pmovusdb256mem_mask(...); +__m128i __builtin_ia32_pmovusdw128_mask(...); +void __builtin_ia32_pmovusdw128mem_mask(...); +__m128i __builtin_ia32_pmovusdw256_mask(...); +void __builtin_ia32_pmovusdw256mem_mask(...); +__m128i __builtin_ia32_pmovusqb128_mask(...); +void __builtin_ia32_pmovusqb128mem_mask(...); +__m128i __builtin_ia32_pmovusqb256_mask(...); +void __builtin_ia32_pmovusqb256mem_mask(...); +__m128i __builtin_ia32_pmovusqw128_mask(...); +void __builtin_ia32_pmovusqw128mem_mask(...); +__m128i __builtin_ia32_pmovusqw256_mask(...); +void __builtin_ia32_pmovusqw256mem_mask(...); +__m256 __builtin_ia32_broadcastf32x4_256_mask(...); +__m256i __builtin_ia32_broadcasti32x4_256_mask(...); +__m256d __builtin_ia32_broadcastsd256_mask(...); +__m128 __builtin_ia32_broadcastss128_mask(...); +__m256 __builtin_ia32_broadcastss256_mask(...); +__m128i __builtin_ia32_pabsb128_mask(...); +__m256i __builtin_ia32_pabsb256_mask(...); +__m128i __builtin_ia32_pbroadcastd128_gpr_mask(...); +__m128i __builtin_ia32_pbroadcastd128_mask(...); +__m256i __builtin_ia32_pbroadcastd256_gpr_mask(...); +__m256i __builtin_ia32_pbroadcastd256_mask(...); +__m512i __builtin_ia32_pbroadcastd512_gpr_mask(...); +__m128i __builtin_ia32_pbroadcastq128_gpr_mask(...); +__m128i __builtin_ia32_pbroadcastq128_mask(...); +__m256i __builtin_ia32_pbroadcastq256_gpr_mask(...); +__m256i __builtin_ia32_pbroadcastq256_mask(...); +__m512i __builtin_ia32_pbroadcastq512_gpr_mask(...); +__mmask8 __builtin_ia32_pcmpeqd256_mask(...); +void __builtin_ia32_pmovsqd128mem_mask(...); +__m128i __builtin_ia32_pmovsqd256_mask(...); +void __builtin_ia32_pmovsqd256mem_mask(...); +__m128i __builtin_ia32_pmovsxbd128_mask(...); +__m256i __builtin_ia32_pmovsxbd256_mask(...); +__m128i __builtin_ia32_pmovsxbq128_mask(...); +__m256i __builtin_ia32_pmovsxbq256_mask(...); +__m128i __builtin_ia32_pmovsxdq128_mask(...); +__m256i __builtin_ia32_pmovsxdq256_mask(...); +__m128i __builtin_ia32_pmovsxwd128_mask(...); +__m256i __builtin_ia32_pmovsxwd256_mask(...); +__m128i __builtin_ia32_pmovsxwq128_mask(...); +__m256i __builtin_ia32_pmovsxwq256_mask(...); +__m128i __builtin_ia32_pmovusqd128_mask(...); +void __builtin_ia32_pmovusqd128mem_mask(...); +__m128i __builtin_ia32_pmovusqd256_mask(...); +void __builtin_ia32_pmovusqd256mem_mask(...); +__m128i __builtin_ia32_pmovzxbd128_mask(...); +__m256i __builtin_ia32_pmovzxbd256_mask(...); +__m128i __builtin_ia32_pmovzxbq128_mask(...); +__m256i __builtin_ia32_pmovzxbq256_mask(...); +__m128i __builtin_ia32_pmovzxwd128_mask(...); +__m256i __builtin_ia32_pmovzxwd256_mask(...); +__m128i __builtin_ia32_pmovzxwq128_mask(...); +__m256i __builtin_ia32_pmovzxwq256_mask(...); +__m128 __builtin_ia32_cvtpd2ps_mask(...); +__m128d __builtin_ia32_getexppd128_mask(...); +__m256d __builtin_ia32_getexppd256_mask(...); +__m128 __builtin_ia32_getexpps128_mask(...); +__m256 __builtin_ia32_getexpps256_mask(...); +__m128i __builtin_ia32_pmovzxdq128_mask(...); +__m256i __builtin_ia32_pmovzxdq256_mask(...); +__m128i __builtin_ia32_psrld128_mask(...); +__m256i __builtin_ia32_psrld256_mask(...); +__m128i __builtin_ia32_psrlq128_mask(...); +__m256i __builtin_ia32_psrlq256_mask(...); +__m128d __builtin_ia32_rcp14pd128_mask(...); +__m256d __builtin_ia32_rcp14pd256_mask(...); +__m512d __builtin_ia32_rcp14pd512_mask(...); +__m128 __builtin_ia32_rcp14ps128_mask(...); +__m256 __builtin_ia32_rcp14ps256_mask(...); +__m512 __builtin_ia32_rcp14ps512_mask(...); +__m128d __builtin_ia32_rsqrt14pd128_mask(...); +__m256d __builtin_ia32_rsqrt14pd256_mask(...); +__m512d __builtin_ia32_rsqrt14pd512_mask(...); +__m128 __builtin_ia32_rsqrt14ps128_mask(...); +__m256 __builtin_ia32_rsqrt14ps256_mask(...); +__m512 __builtin_ia32_rsqrt14ps512_mask(...); +__m128d __builtin_ia32_scalefpd128_mask(...); +__m256d __builtin_ia32_scalefpd256_mask(...); +__m128 __builtin_ia32_scalefps128_mask(...); +__m256 __builtin_ia32_scalefps256_mask(...); +__m128d __builtin_ia32_sqrtpd128_mask(...); +__m256d __builtin_ia32_sqrtpd256_mask(...); +__m128 __builtin_ia32_sqrtps128_mask(...); +__m256 __builtin_ia32_sqrtps256_mask(...); +__m128d __builtin_ia32_compressdf128_mask(...); +__m256d __builtin_ia32_compressdf256_mask(...); +__m128i __builtin_ia32_compressdi128_mask(...); +__m256i __builtin_ia32_compressdi256_mask(...); +__m128 __builtin_ia32_compresssf128_mask(...); +__m256 __builtin_ia32_compresssf256_mask(...); +__m128i __builtin_ia32_compresssi128_mask(...); +__m256i __builtin_ia32_compresssi256_mask(...); +void __builtin_ia32_compressstoredf128_mask(...); +void __builtin_ia32_compressstoredf256_mask(...); +void __builtin_ia32_compressstoredi128_mask(...); +void __builtin_ia32_compressstoredi256_mask(...); +void __builtin_ia32_compressstoresf128_mask(...); +void __builtin_ia32_compressstoresf256_mask(...); +void __builtin_ia32_compressstoresi128_mask(...); +void __builtin_ia32_compressstoresi256_mask(...); +__m128 __builtin_ia32_cvtpd2ps256_mask(...); +__m128i __builtin_ia32_cvtps2dq128_mask(...); +__m256i __builtin_ia32_cvtps2dq256_mask(...); +__m128i __builtin_ia32_cvtps2udq128_mask(...); +__m256i __builtin_ia32_cvtps2udq256_mask(...); +__m128d __builtin_ia32_expanddf128_mask(...); +__m128d __builtin_ia32_expanddf128_maskz(...); +__m256d __builtin_ia32_expanddf256_mask(...); +__m256d __builtin_ia32_expanddf256_maskz(...); +__m256d __builtin_ia32_expandloaddf256_mask(...); +__m256d __builtin_ia32_expandloaddf256_maskz(...); +__m128d __builtin_ia32_movddup128_mask(...); +__m256d __builtin_ia32_movddup256_mask(...); +__m128 __builtin_ia32_movshdup128_mask(...); +__m256 __builtin_ia32_movshdup256_mask(...); +__m128 __builtin_ia32_movsldup128_mask(...); +__m256 __builtin_ia32_movsldup256_mask(...); +__m128i __builtin_ia32_pandq128_mask(...); +__m256i __builtin_ia32_pandq256_mask(...); +__mmask8 __builtin_ia32_ptestmd128(...); +__mmask8 __builtin_ia32_ptestmd256(...); +__mmask16 __builtin_ia32_ptestmd512(...); +__mmask8 __builtin_ia32_ptestmq128(...); +__mmask8 __builtin_ia32_ptestmq256(...); +__mmask8 __builtin_ia32_ptestmq512(...); +__mmask8 __builtin_ia32_ptestnmd128(...); +__mmask8 __builtin_ia32_ptestnmd256(...); +__mmask8 __builtin_ia32_ptestnmq128(...); +__mmask8 __builtin_ia32_ptestnmq256(...); +__m128i __builtin_ia32_punpckhdq128_mask(...); +__m256i __builtin_ia32_punpckhdq256_mask(...); +__m128i __builtin_ia32_punpckhqdq128_mask(...); +__m256i __builtin_ia32_punpckhqdq256_mask(...); +__m128i __builtin_ia32_punpckldq128_mask(...); +__m256i __builtin_ia32_punpckldq256_mask(...); +__m128i __builtin_ia32_punpcklqdq128_mask(...); +__m256i __builtin_ia32_punpcklqdq256_mask(...); + +__m128i __builtin_ia32_expanddi128_mask(...); +__m128i __builtin_ia32_expanddi128_maskz(...); +__m256i __builtin_ia32_expanddi256_mask(...); +__m256i __builtin_ia32_expanddi256_maskz(...); +__m128d __builtin_ia32_expandloaddf128_mask(...); +__m128d __builtin_ia32_expandloaddf128_maskz(...); +__m128i __builtin_ia32_expandloaddi128_mask(...); +__m128i __builtin_ia32_expandloaddi128_maskz(...); +__m256i __builtin_ia32_expandloaddi256_mask(...); +__m256i __builtin_ia32_expandloaddi256_maskz(...); +__m128 __builtin_ia32_expandloadsf128_mask(...); +__m128 __builtin_ia32_expandloadsf128_maskz(...); +__m256 __builtin_ia32_expandloadsf256_mask(...); +__m256 __builtin_ia32_expandloadsf256_maskz(...); +__m128i __builtin_ia32_expandloadsi128_mask(...); +__m128i __builtin_ia32_expandloadsi128_maskz(...); +__m256i __builtin_ia32_expandloadsi256_mask(...); +__m256i __builtin_ia32_expandloadsi256_maskz(...); +__m128 __builtin_ia32_expandsf128_mask(...); +__m128 __builtin_ia32_expandsf128_maskz(...); +__m256 __builtin_ia32_expandsf256_mask(...); +__m256 __builtin_ia32_expandsf256_maskz(...); +__m128i __builtin_ia32_expandsi128_mask(...); +__m128i __builtin_ia32_expandsi128_maskz(...); +__m256i __builtin_ia32_expandsi256_mask(...); +__m256i __builtin_ia32_expandsi256_maskz(...); +__m128i __builtin_ia32_prolvd128_mask(...); +__m256i __builtin_ia32_prolvd256_mask(...); +__m256i __builtin_ia32_prolvq256_mask(...); +__m128i __builtin_ia32_prorvd128_mask(...); +__m256i __builtin_ia32_prorvd256_mask(...); +__m128i __builtin_ia32_psllv2di_mask(...); +__m256i __builtin_ia32_psllv4di_mask(...); +__m128i __builtin_ia32_psllv4si_mask(...); +__m256i __builtin_ia32_psllv8si_mask(...); +__m128i __builtin_ia32_psrav4si_mask(...); +__m256i __builtin_ia32_psrav8si_mask(...); +__m128i __builtin_ia32_psravq128_mask(...); +__m128i __builtin_ia32_psrlv2di_mask(...); +__m256i __builtin_ia32_psrlv4di_mask(...); +__m128i __builtin_ia32_psrlv4si_mask(...); +__m256i __builtin_ia32_psrlv8si_mask(...); +__m128i __builtin_ia32_vpermi2vard128_mask(...); +__m256i __builtin_ia32_vpermi2vard256_mask(...); +__m128i __builtin_ia32_vpermi2varhi128_mask(...); +__m256i __builtin_ia32_vpermi2varhi256_mask(...); +__m128d __builtin_ia32_vpermi2varpd128_mask(...); +__m256d __builtin_ia32_vpermi2varpd256_mask(...); +__m128 __builtin_ia32_vpermi2varps128_mask(...); +__m256 __builtin_ia32_vpermi2varps256_mask(...); +__m128i __builtin_ia32_vpermi2varq128_mask(...); +__m256i __builtin_ia32_vpermi2varq256_mask(...); +__m128i __builtin_ia32_vpermt2vard128_mask(...); +__m128i __builtin_ia32_vpermt2vard128_maskz(...); +__m256i __builtin_ia32_vpermt2vard256_mask(...); +__m256i __builtin_ia32_vpermt2vard256_maskz(...); +__m128i __builtin_ia32_vpermt2varhi128_mask(...); +__m128i __builtin_ia32_vpermt2varhi128_maskz(...); +__m256i __builtin_ia32_vpermt2varhi256_mask(...); +__m256i __builtin_ia32_vpermt2varhi256_maskz(...); +__m128d __builtin_ia32_vpermt2varpd128_mask(...); +__m128d __builtin_ia32_vpermt2varpd128_maskz(...); +__m256d __builtin_ia32_vpermt2varpd256_mask(...); +__m256d __builtin_ia32_vpermt2varpd256_maskz(...); +__m128 __builtin_ia32_vpermt2varps128_mask(...); +__m128 __builtin_ia32_vpermt2varps128_maskz(...); +__m256 __builtin_ia32_vpermt2varps256_mask(...); +__m256 __builtin_ia32_vpermt2varps256_maskz(...); +__m128i __builtin_ia32_vpermt2varq128_mask(...); +__m128i __builtin_ia32_vpermt2varq128_maskz(...); +__m256i __builtin_ia32_vpermt2varq256_mask(...); +__m256i __builtin_ia32_vpermt2varq256_maskz(...); +__m256d __builtin_ia32_divpd256_mask(...); +__m128d __builtin_ia32_divpd_mask(...); +__m256 __builtin_ia32_divps256_mask(...); +__m128 __builtin_ia32_divps_mask(...); +__m512d __builtin_ia32_exp2pd_mask(...); +__m512 __builtin_ia32_exp2ps_mask(...); +__m256d __builtin_ia32_maxpd256_mask(...); +__m128d __builtin_ia32_maxpd_mask(...); +__m256 __builtin_ia32_maxps256_mask(...); +__m128 __builtin_ia32_maxps_mask(...); +__m256d __builtin_ia32_minpd256_mask(...); +__m128d __builtin_ia32_minpd_mask(...); +__m256 __builtin_ia32_minps256_mask(...); +__m128 __builtin_ia32_minps_mask(...); +__m256d __builtin_ia32_mulpd256_mask(...); +__m128d __builtin_ia32_mulpd_mask(...); +__m256 __builtin_ia32_mulps256_mask(...); +__m128 __builtin_ia32_mulps_mask(...); +__m128i __builtin_ia32_pmaxsd128_mask(...); +__m256i __builtin_ia32_pmaxsd256_mask(...); +__m128i __builtin_ia32_pmaxsq128_mask(...); +__m256i __builtin_ia32_pmaxsq256_mask(...); +__m256i __builtin_ia32_pmaxud256_mask(...); +__m128i __builtin_ia32_pmaxuq128_mask(...); +__m256i __builtin_ia32_pmaxuq256_mask(...); +__m128i __builtin_ia32_pminsd128_mask(...); +__m256i __builtin_ia32_pminsd256_mask(...); +__m128i __builtin_ia32_pminsq128_mask(...); +__m256i __builtin_ia32_pminsq256_mask(...); +__m256i __builtin_ia32_pminud256_mask(...); +__m128i __builtin_ia32_pminuq128_mask(...); +__m256i __builtin_ia32_pminuq256_mask(...); +__m256i __builtin_ia32_pmulld256_mask(...); +__m128i __builtin_ia32_prolvq128_mask(...); +__m128i __builtin_ia32_prorvq128_mask(...); +__m256i __builtin_ia32_prorvq256_mask(...); +__m256i __builtin_ia32_psravq256_mask(...); +__m128i __builtin_ia32_broadcastmb128(...); +__m256i __builtin_ia32_broadcastmb256(...); +__m128i __builtin_ia32_broadcastmw128(...); +__m256i __builtin_ia32_broadcastmw256(...); +__m128i __builtin_ia32_pbroadcastb128(...); +__m256i __builtin_ia32_pbroadcastb256(...); +__m256i __builtin_ia32_pbroadcastw256(...); +__m128i __builtin_ia32_pmaxud128_mask(...); +__m128i __builtin_ia32_pminud128_mask(...); +__m128i __builtin_ia32_psrad128_mask(...); +__m256i __builtin_ia32_psrad256_mask(...); +__m128i __builtin_ia32_psraq128_mask(...); +__m256i __builtin_ia32_psraq256_mask(...); +__mmask8 __builtin_ia32_ucmpd128_mask(...); +__mmask8 __builtin_ia32_ucmpd256_mask(...); +__m128d __builtin_ia32_unpckhpd128_mask(...); +__m256d __builtin_ia32_unpckhpd256_mask(...); +__m128 __builtin_ia32_unpckhps128_mask(...); +__m256 __builtin_ia32_unpckhps256_mask(...); +__m128d __builtin_ia32_unpcklpd128_mask(...); +__m256d __builtin_ia32_unpcklpd256_mask(...); +__m128 __builtin_ia32_unpcklps128_mask(...); +__m256 __builtin_ia32_unpcklps256_mask(...); +__m256 __builtin_ia32_vcvtph2ps256_mask(...); +__m128 __builtin_ia32_vcvtph2ps_mask(...); +__m128i __builtin_ia32_vpconflictdi_128_mask(...); +__m256i __builtin_ia32_vpconflictdi_256_mask(...); +__m512i __builtin_ia32_vpconflictdi_512_mask(...); +__m128i __builtin_ia32_vpconflictsi_128_mask(...); +__m256i __builtin_ia32_vpconflictsi_256_mask(...); +__m512i __builtin_ia32_vpconflictsi_512_mask(...); +__m128i __builtin_ia32_vplzcntd_128_mask(...); +__m256i __builtin_ia32_vplzcntd_256_mask(...); +__m512i __builtin_ia32_vplzcntd_512_mask(...); +__m128i __builtin_ia32_vplzcntq_128_mask(...); +__m256i __builtin_ia32_vplzcntq_256_mask(...); +__m512i __builtin_ia32_vplzcntq_512_mask(...); +__mmask64 __builtin_ia32_kunpckdi(...); +__mmask32 __builtin_ia32_kunpcksi(...); +__m512i __builtin_ia32_loaddquhi512_mask(...); +__m512i __builtin_ia32_loaddquqi512_mask(...); +__m512i __builtin_ia32_movdquhi512_mask(...); +__m512i __builtin_ia32_movdquqi512_mask(...); +__m512i __builtin_ia32_pbroadcastb512_gpr_mask(...); +__m512i __builtin_ia32_pbroadcastb512_mask(...); +__m512i __builtin_ia32_pbroadcastw512_gpr_mask(...); +__m512i __builtin_ia32_pbroadcastw512_mask(...); +__m256d __builtin_ia32_permvardf256_mask(...); +__m256i __builtin_ia32_permvardi256_mask(...); +__m512i __builtin_ia32_permvarhi512_mask(...); +__m256 __builtin_ia32_permvarsf256_mask(...); +__m256i __builtin_ia32_permvarsi256_mask(...); +__m512i __builtin_ia32_pmaddubsw512_mask(...); +__m512i __builtin_ia32_pmaddwd512_mask(...); +__m256i __builtin_ia32_pmovswb512_mask(...); +__m512i __builtin_ia32_pmovsxbw512_mask(...); +__m256i __builtin_ia32_pmovuswb512_mask(...); +__m256i __builtin_ia32_pmovwb512_mask(...); +__m512i __builtin_ia32_pmovzxbw512_mask(...); +__m512i __builtin_ia32_pmulhrsw512_mask(...); +__m512i __builtin_ia32_pmulhuw512_mask(...); +__m512i __builtin_ia32_pmulhw512_mask(...); +__m128i __builtin_ia32_pmulld128_mask(...); +__m128i __builtin_ia32_psadbw128(...); +__m512i __builtin_ia32_psadbw512(...); +__m128i __builtin_ia32_pslld128_mask(...); +__m256i __builtin_ia32_pslld256_mask(...); +__m128i __builtin_ia32_psllq128_mask(...); +__m256i __builtin_ia32_psllq256_mask(...); +__m512i __builtin_ia32_psllw512_mask(...); +__m512i __builtin_ia32_psrlw512_mask(...); +__m256i __builtin_ia32_psubd256_mask(...); +__m512i __builtin_ia32_punpckhbw512_mask(...); +void __builtin_ia32_storedquhi512_mask(...); +void __builtin_ia32_storedquqi512_mask(...); +__m512i __builtin_ia32_vpermi2varhi512_mask(...); +__m256d __builtin_ia32_vpermilvarpd256_mask(...); +__m128d __builtin_ia32_vpermilvarpd_mask(...); +__m256 __builtin_ia32_vpermilvarps256_mask(...); +__m128 __builtin_ia32_vpermilvarps_mask(...); +__m512 __builtin_ia32_broadcastf32x2_512_mask(...); +__m512 __builtin_ia32_broadcastf32x8_512_mask(...); +__m512d __builtin_ia32_broadcastf64x2_512_mask(...); +__m512i __builtin_ia32_broadcasti32x2_512_mask(...); +__m512i __builtin_ia32_broadcasti32x8_512_mask(...); +__m512i __builtin_ia32_broadcasti64x2_512_mask(...); +__mmask64 __builtin_ia32_cvtb2mask512(...); +__mmask16 __builtin_ia32_cvtd2mask512(...); +__m512i __builtin_ia32_cvtmask2b512(...); +__m512i __builtin_ia32_cvtmask2d512(...); +__m512i __builtin_ia32_cvtmask2q512(...); +__m512i __builtin_ia32_cvtmask2w512(...); +__m512i __builtin_ia32_cvtpd2qq512_mask(...); +__m512i __builtin_ia32_cvtpd2uqq512_mask(...); +__m512i __builtin_ia32_cvtps2qq512_mask(...); +__m512i __builtin_ia32_cvtps2uqq512_mask(...); +__mmask8 __builtin_ia32_cvtq2mask512(...); +__m512d __builtin_ia32_cvtqq2pd512_mask(...); +__m256 __builtin_ia32_cvtqq2ps512_mask(...); +__m512i __builtin_ia32_cvttpd2qq512_mask(...); +__m512i __builtin_ia32_cvttpd2uqq512_mask(...); +__m512i __builtin_ia32_cvttps2qq512_mask(...); +__m512i __builtin_ia32_cvttps2uqq512_mask(...); +__m512d __builtin_ia32_cvtuqq2pd512_mask(...); +__m256 __builtin_ia32_cvtuqq2ps512_mask(...); +__mmask32 __builtin_ia32_cvtw2mask512(...); +__m512i __builtin_ia32_psllv32hi_mask(...); +__m512i __builtin_ia32_psrav32hi_mask(...); +__m512i __builtin_ia32_psraw512_mask(...); +__m512i __builtin_ia32_psrlv32hi_mask(...); +__m512i __builtin_ia32_psubw512_mask(...); +__mmask64 __builtin_ia32_ptestmb512(...); +__mmask32 __builtin_ia32_ptestmw512(...); +__mmask64 __builtin_ia32_ptestnmb512(...); +__mmask32 __builtin_ia32_ptestnmw512(...); +__m512i __builtin_ia32_punpckhwd512_mask(...); +__m512i __builtin_ia32_punpcklbw512_mask(...); +__m512i __builtin_ia32_punpcklwd512_mask(...); +__mmask16 __builtin_ia32_cvtb2mask128(...); +__mmask32 __builtin_ia32_cvtb2mask256(...); +__m128i __builtin_ia32_cvtmask2b128(...); +__m256i __builtin_ia32_cvtmask2b256(...); +__m128i __builtin_ia32_cvtmask2w128(...); +__m256i __builtin_ia32_cvtmask2w256(...); +__mmask8 __builtin_ia32_cvtw2mask128(...); +__mmask16 __builtin_ia32_cvtw2mask256(...); +__m128i __builtin_ia32_loaddquhi128_mask(...); +__m256i __builtin_ia32_loaddquhi256_mask(...); +__m128i __builtin_ia32_loaddquqi128_mask(...); +__m256i __builtin_ia32_loaddquqi256_mask(...); +__m128i __builtin_ia32_movdquhi128_mask(...); +__m256i __builtin_ia32_movdquhi256_mask(...); +__m128i __builtin_ia32_movdquqi128_mask(...); +__m256i __builtin_ia32_movdquqi256_mask(...); +__m128i __builtin_ia32_pbroadcastb128_gpr_mask(...); +__m128i __builtin_ia32_pbroadcastb128_mask(...); +__m256i __builtin_ia32_pbroadcastb256_gpr_mask(...); +__m256i __builtin_ia32_pbroadcastb256_mask(...); +__m128i __builtin_ia32_pbroadcastw128_gpr_mask(...); +__m128i __builtin_ia32_pbroadcastw128_mask(...); +__m256i __builtin_ia32_pbroadcastw256_gpr_mask(...); +__m256i __builtin_ia32_pbroadcastw256_mask(...); +__mmask32 __builtin_ia32_pcmpgtb256_mask(...); +__m128i __builtin_ia32_permvarhi128_mask(...); +__m256i __builtin_ia32_permvarhi256_mask(...); +__m128i __builtin_ia32_pmaddubsw128_mask(...); +__m256i __builtin_ia32_pmaddubsw256_mask(...); +__m128i __builtin_ia32_pmaddwd128_mask(...); +__m256i __builtin_ia32_pmaddwd256_mask(...); +__m128i __builtin_ia32_pmovswb128_mask(...); +__m128i __builtin_ia32_pmovswb256_mask(...); +__m256i __builtin_ia32_pmovsxbw256_mask(...); +__m128i __builtin_ia32_pmovuswb128_mask(...); +__m128i __builtin_ia32_pmovuswb256_mask(...); +__m128i __builtin_ia32_pmovwb256_mask(...); +__m128i __builtin_ia32_pmulhrsw128_mask(...); +__m256i __builtin_ia32_pmulhrsw256_mask(...); +__m128i __builtin_ia32_pmulhuw128_mask(...); +__m256i __builtin_ia32_pmulhuw256_mask(...); +__m128i __builtin_ia32_pmulhw128_mask(...); +__m256i __builtin_ia32_pmulhw256_mask(...); +__mmask16 __builtin_ia32_ptestmb128(...); +__mmask32 __builtin_ia32_ptestmb256(...); +__mmask8 __builtin_ia32_ptestmw128(...); +__mmask16 __builtin_ia32_ptestmw256(...); +void __builtin_ia32_storedquqi128_mask(...); +void __builtin_ia32_storedquqi256_mask(...); +__m128i __builtin_ia32_cvtpd2qq128_mask(...); +__m256i __builtin_ia32_cvtpd2qq256_mask(...); +__m128i __builtin_ia32_cvtpd2uqq128_mask(...); +__m256i __builtin_ia32_cvtpd2uqq256_mask(...); +__m128i __builtin_ia32_cvttpd2qq128_mask(...); +__m256i __builtin_ia32_cvttpd2qq256_mask(...); +__m128i __builtin_ia32_cvttpd2uqq128_mask(...); +__m256i __builtin_ia32_cvttpd2uqq256_mask(...); +__mmask16 __builtin_ia32_pcmpgtb128_mask(...); +__m128i __builtin_ia32_pmovsxbw128_mask(...); +__m128i __builtin_ia32_pmovwb128_mask(...); +__m128i __builtin_ia32_pmovzxbw128_mask(...); +__m256i __builtin_ia32_pmovzxbw256_mask(...); +__m256i __builtin_ia32_psllv16hi_mask(...); +__m128i __builtin_ia32_psllv8hi_mask(...); +__m128i __builtin_ia32_psllw128_mask(...); +__m256i __builtin_ia32_psllw256_mask(...); +__m256i __builtin_ia32_psrav16hi_mask(...); +__m128i __builtin_ia32_psrav8hi_mask(...); +__m128i __builtin_ia32_psraw128_mask(...); +__m256i __builtin_ia32_psraw256_mask(...); +__m256i __builtin_ia32_psrlv16hi_mask(...); +__m128i __builtin_ia32_psrlv8hi_mask(...); +__m128i __builtin_ia32_psrlw128_mask(...); +__m256i __builtin_ia32_psrlw256_mask(...); +__m128i __builtin_ia32_psubw128_mask(...); +__m256i __builtin_ia32_psubw256_mask(...); +__mmask16 __builtin_ia32_ptestnmb128(...); +__mmask32 __builtin_ia32_ptestnmb256(...); +__mmask8 __builtin_ia32_ptestnmw128(...); +__mmask16 __builtin_ia32_ptestnmw256(...); +__m128i __builtin_ia32_punpckhbw128_mask(...); +__m256i __builtin_ia32_punpckhbw256_mask(...); +__m128i __builtin_ia32_punpckhwd128_mask(...); +__m256i __builtin_ia32_punpckhwd256_mask(...); +__m128i __builtin_ia32_punpcklbw128_mask(...); +__m256i __builtin_ia32_punpcklbw256_mask(...); +__m128i __builtin_ia32_punpcklwd128_mask(...); +__m256i __builtin_ia32_punpcklwd256_mask(...); +void __builtin_ia32_storedquhi128_mask(...); +void __builtin_ia32_storedquhi256_mask(...); +__m256 __builtin_ia32_broadcastf32x2_256_mask(...); +__m256d __builtin_ia32_broadcastf64x2_256_mask(...); +__m128i __builtin_ia32_broadcasti32x2_128_mask(...); +__m256i __builtin_ia32_broadcasti32x2_256_mask(...); +__m256i __builtin_ia32_broadcasti64x2_256_mask(...); +__mmask8 __builtin_ia32_cvtd2mask128(...); +__mmask8 __builtin_ia32_cvtd2mask256(...); +__m128i __builtin_ia32_cvtmask2d128(...); +__m256i __builtin_ia32_cvtmask2d256(...); +__m128i __builtin_ia32_cvtmask2q128(...); +__m256i __builtin_ia32_cvtmask2q256(...); +__m128i __builtin_ia32_cvtps2qq128_mask(...); +__m256i __builtin_ia32_cvtps2qq256_mask(...); +__m128i __builtin_ia32_cvtps2uqq128_mask(...); +__m256i __builtin_ia32_cvtps2uqq256_mask(...); +__mmask8 __builtin_ia32_cvtq2mask128(...); +__mmask8 __builtin_ia32_cvtq2mask256(...); +__m128d __builtin_ia32_cvtqq2pd128_mask(...); +__m256d __builtin_ia32_cvtqq2pd256_mask(...); +__m128 __builtin_ia32_cvtqq2ps128_mask(...); +__m128 __builtin_ia32_cvtqq2ps256_mask(...); +__m128i __builtin_ia32_cvttps2qq128_mask(...); +__m256i __builtin_ia32_cvttps2qq256_mask(...); +__m128i __builtin_ia32_cvttps2uqq128_mask(...); +__m256i __builtin_ia32_cvttps2uqq256_mask(...); +__m128d __builtin_ia32_cvtuqq2pd128_mask(...); +__m256d __builtin_ia32_cvtuqq2pd256_mask(...); +__m128 __builtin_ia32_cvtuqq2ps128_mask(...); +__m128 __builtin_ia32_cvtuqq2ps256_mask(...); +__m512i __builtin_ia32_permvarqi512_mask(...); +__m128i __builtin_ia32_vpmadd52huq128_mask(...); +__m128i __builtin_ia32_vpmadd52huq128_maskz(...); +__m256i __builtin_ia32_vpmadd52huq256_mask(...); +__m256i __builtin_ia32_vpmadd52huq256_maskz(...); +__m512i __builtin_ia32_vpmadd52huq512_mask(...); +__m512i __builtin_ia32_vpmadd52huq512_maskz(...); +__m128i __builtin_ia32_vpmadd52luq128_mask(...); +__m128i __builtin_ia32_vpmadd52luq128_maskz(...); +__m256i __builtin_ia32_vpmadd52luq256_mask(...); +__m256i __builtin_ia32_vpmadd52luq256_maskz(...); +__m512i __builtin_ia32_vpmadd52luq512_mask(...); +__m512i __builtin_ia32_vpmadd52luq512_maskz(...); +__m512i __builtin_ia32_vpmultishiftqb512_mask(...); +__m64 __builtin_ia32_pcmpeqb(...); +__m64 __builtin_ia32_pcmpeqw(...); +__m128i __builtin_ia32_permvarqi128_mask(...); +__m256i __builtin_ia32_permvarqi256_mask(...); +int __builtin_ia32_vec_ext_v2si(...); +int __builtin_ia32_vec_ext_v4sf(...); +__m128d __builtin_ia32_vfmaddsd(...); +__m128d __builtin_ia32_vfmaddsd3(...); +__m128 __builtin_ia32_vfmaddss(...); +__m128 __builtin_ia32_vfmaddss3(...); +__m128i __builtin_ia32_vpcomeqb(...); +__m128i __builtin_ia32_vpcomequb(...); +__m128i __builtin_ia32_vpcomequd(...); +__m128i __builtin_ia32_vpcomequq(...); +__m128i __builtin_ia32_vpcomequw(...); +__m128i __builtin_ia32_vpcomeqw(...); +__m128i __builtin_ia32_vpcomfalseb(...); +__m128i __builtin_ia32_vpcomfalseub(...); +__m128i __builtin_ia32_vpcomfalseud(...); +__m128i __builtin_ia32_vpcomfalseuq(...); +__m128i __builtin_ia32_vpcomfalseuw(...); +__m128i __builtin_ia32_vpcomgeb(...); +__m128i __builtin_ia32_vpcomgeub(...); +__m128i __builtin_ia32_vpcomgeud(...); +__m128i __builtin_ia32_vpcomgeuq(...); +__m128i __builtin_ia32_vpcomgeuw(...); +__m128i __builtin_ia32_vpcomgew(...); +__m128i __builtin_ia32_vpcomgtb(...); +__m128i __builtin_ia32_vpcomgtub(...); +__m128i __builtin_ia32_vpcomgtud(...); +__m128i __builtin_ia32_vpcomgtuq(...); +__m128i __builtin_ia32_vpcomgtuw(...); +__m128i __builtin_ia32_vpcomgtw(...); +__m128i __builtin_ia32_vpcomleb(...); +__m128i __builtin_ia32_vpcomleub(...); +__m128i __builtin_ia32_vpcomleud(...); +__m128i __builtin_ia32_vpcomleuq(...); +__m128i __builtin_ia32_vpcomleuw(...); +__m128i __builtin_ia32_vpcomlew(...); +__m128i __builtin_ia32_vpcomltb(...); +__m128i __builtin_ia32_vpcomltub(...); +__m128i __builtin_ia32_vpcomltud(...); +__m128i __builtin_ia32_vpcomltuq(...); +__m128i __builtin_ia32_vpcomltuw(...); +__m128i __builtin_ia32_vpcomltw(...); +__m128i __builtin_ia32_vpcomneqb(...); +__m128i __builtin_ia32_vpcomnequb(...); +__m128i __builtin_ia32_vpcomnequd(...); +__m128i __builtin_ia32_vpcomnequq(...); +__m128i __builtin_ia32_vpcomnequw(...); +__m128i __builtin_ia32_vpcomneqw(...); +__m128i __builtin_ia32_vpcomtrueb(...); +__m128i __builtin_ia32_vpcomtrueub(...); +__m128i __builtin_ia32_vpcomtrueud(...); +__m128i __builtin_ia32_vpcomtrueuq(...); +__m128i __builtin_ia32_vpcomtrueuw(...); +__m128i __builtin_ia32_vpermi2varqi128_mask(...); +__m256i __builtin_ia32_vpermi2varqi256_mask(...); +__m512i __builtin_ia32_vpermi2varqi512_mask(...); +__m512i __builtin_ia32_vpermt2varhi512_mask(...); +__m512i __builtin_ia32_vpermt2varhi512_maskz(...); +__m128i __builtin_ia32_vpermt2varqi128_mask(...); +__m128i __builtin_ia32_vpermt2varqi128_maskz(...); +__m256i __builtin_ia32_vpermt2varqi256_mask(...); +__m256i __builtin_ia32_vpermt2varqi256_maskz(...); +__m512i __builtin_ia32_vpermt2varqi512_mask(...); +__m512i __builtin_ia32_vpermt2varqi512_maskz(...); +__m128i __builtin_ia32_vpmultishiftqb128_mask(...); +__m256i __builtin_ia32_vpmultishiftqb256_mask(...); +__m128i __builtin_ia32_vprotb(...); +__m128i __builtin_ia32_vprotd(...); +__m128i __builtin_ia32_vprotw(...); +void __builtin_ia32_clflush(...); +void __builtin_ia32_clflushopt(...); +void __builtin_ia32_clwb(...); +void __builtin_ia32_llwpcb(...); +void __builtin_ia32_monitor(...); +void __builtin_ia32_monitorx(...); +void __builtin_ia32_mwaitx(...); +__m64 __builtin_ia32_pcmpeqd(...); +__m64 __builtin_ia32_pcmpgtd(...); +void __builtin_ia32_pcommit(...); +__m64 __builtin_ia32_pfsubr(...); +int __builtin_ia32_rdseed_di_step(...); +int __builtin_ia32_rdseed_hi_step(...); +int __builtin_ia32_rdseed_si_step(...); +unsigned char __builtin_ia32_sbb_u32(...); +unsigned char __builtin_ia32_sbb_u64(...); +void __builtin_ia32_sfence(...); +void * __builtin_ia32_slwpcb(...); +__m128i __builtin_ia32_vpcomeqd(...); +__m128i __builtin_ia32_vpcomeqq(...); +__m128i __builtin_ia32_vpcomfalsed(...); +__m128i __builtin_ia32_vpcomfalseq(...); +__m128i __builtin_ia32_vpcomfalsew(...); +__m128i __builtin_ia32_vpcomged(...); +__m128i __builtin_ia32_vpcomgeq(...); +__m128i __builtin_ia32_vpcomgtd(...); +__m128i __builtin_ia32_vpcomgtq(...); +__m128i __builtin_ia32_vpcomled(...); +__m128i __builtin_ia32_vpcomleq(...); +__m128i __builtin_ia32_vpcomltd(...); +__m128i __builtin_ia32_vpcomltq(...); +__m128i __builtin_ia32_vpcomneqd(...); +__m128i __builtin_ia32_vpcomneqq(...); +__m128i __builtin_ia32_vpcomtrued(...); +__m128i __builtin_ia32_vpcomtrueq(...); +__m128i __builtin_ia32_vpcomtruew(...); +__m128i __builtin_ia32_vprotq(...); +void __builtin_ia32_xrstor(...); +void __builtin_ia32_xrstor64(...); +void __builtin_ia32_xrstors(...); +void __builtin_ia32_xrstors64(...); +void __builtin_ia32_xsave(...); +void __builtin_ia32_xsave64(...); +void __builtin_ia32_xsavec(...); +void __builtin_ia32_xsavec64(...); +void __builtin_ia32_xsaveopt(...); +void __builtin_ia32_xsaveopt64(...); +void __builtin_ia32_xsaves(...); +void __builtin_ia32_xsaves64(...); + +} diff --git a/languages/clang/duchain/parsesession.cpp b/languages/clang/duchain/parsesession.cpp index 652b04b05d..1abde7d54a 100644 --- a/languages/clang/duchain/parsesession.cpp +++ b/languages/clang/duchain/parsesession.cpp @@ -1,354 +1,370 @@ /* This file is part of KDevelop Copyright 2013 Olivier de Gaalon Copyright 2013 Milian Wolff Copyright 2013 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "parsesession.h" +#include #include "clangproblem.h" #include "clangdiagnosticevaluator.h" #include "todoextractor.h" #include "clanghelpers.h" #include "clangindex.h" #include "clangparsingenvironment.h" #include "util/clangdebug.h" #include "util/clangtypes.h" #include "util/clangutils.h" #include #include #include #include #include #include #include using namespace KDevelop; namespace { QVector argsForSession(const QString& path, ParseSessionData::Options options, const ParserSettings& parserSettings) { QMimeDatabase db; if(db.mimeTypeForFile(path).name() == QStringLiteral("text/x-objcsrc")) { return {QByteArrayLiteral("-xobjective-c++")}; } if (parserSettings.parserOptions.isEmpty()) { // The parserOptions can be empty for some unit tests that use ParseSession directly auto defaultArguments = ClangSettingsManager::self()->parserSettings(nullptr).toClangAPI(); Q_ASSERT(!defaultArguments.isEmpty()); defaultArguments.append(QByteArrayLiteral("-nostdinc")); defaultArguments.append(QByteArrayLiteral("-nostdinc++")); defaultArguments.append(QByteArrayLiteral("-xc++")); return defaultArguments; } auto result = parserSettings.toClangAPI(); result.append(QByteArrayLiteral("-nostdinc")); result.append(QByteArrayLiteral("-nostdinc++")); if (options & ParseSessionData::PrecompiledHeader) { result.append(parserSettings.isCpp() ? QByteArrayLiteral("-xc++-header") : QByteArrayLiteral("-xc-header")); return result; } result.append(parserSettings.isCpp() ? QByteArrayLiteral("-xc++") : QByteArrayLiteral("-xc")); return result; } void addIncludes(QVector* args, QVector* otherArgs, const Path::List& includes, const char* cliSwitch) { foreach (const Path& url, includes) { if (url.isEmpty()) { continue; } QFileInfo info(url.toLocalFile()); QByteArray path = url.toLocalFile().toUtf8(); if (info.isFile()) { path.prepend("-include"); } else { path.prepend(cliSwitch); } otherArgs->append(path); args->append(path.constData()); } } QVector toClangApi(const QVector& unsavedFiles) { QVector unsaved; unsaved.reserve(unsavedFiles.size()); std::transform(unsavedFiles.begin(), unsavedFiles.end(), std::back_inserter(unsaved), [] (const UnsavedFile& file) { return file.toClangApi(); }); return unsaved; } +bool needGccCompatibility(const ClangParsingEnvironment& environment) +{ + const auto& defines = environment.defines(); + // TODO: potentially do the same for the intel compiler? + return defines.contains(QStringLiteral("__GNUC__")) + && !environment.defines().contains(QStringLiteral("__clang__")); +} + } ParseSessionData::ParseSessionData(const QVector& unsavedFiles, ClangIndex* index, const ClangParsingEnvironment& environment, Options options) : m_file(nullptr) , m_unit(nullptr) { unsigned int flags = CXTranslationUnit_CXXChainedPCH | CXTranslationUnit_DetailedPreprocessingRecord; if (options.testFlag(SkipFunctionBodies)) { flags |= CXTranslationUnit_SkipFunctionBodies; } if (options.testFlag(PrecompiledHeader)) { flags |= CXTranslationUnit_ForSerialization; } else { flags |= CXTranslationUnit_CacheCompletionResults | CXTranslationUnit_PrecompiledPreamble; if (environment.quality() == ClangParsingEnvironment::Unknown) { flags |= CXTranslationUnit_Incomplete; } } const auto tuUrl = environment.translationUnitUrl(); Q_ASSERT(!tuUrl.isEmpty()); const auto arguments = argsForSession(tuUrl.str(), options, environment.parserSettings()); QVector clangArguments; const auto& includes = environment.includes(); const auto& pchInclude = environment.pchInclude(); // uses QByteArray as smart-pointer for const char* ownership QVector smartArgs; smartArgs.reserve(includes.system.size() + includes.project.size() + pchInclude.isValid() + arguments.size() + 1); clangArguments.reserve(smartArgs.size()); std::transform(arguments.constBegin(), arguments.constEnd(), std::back_inserter(clangArguments), [] (const QByteArray &argument) { return argument.constData(); }); // NOTE: the PCH include must come before all other includes! if (pchInclude.isValid()) { clangArguments << "-include"; QByteArray pchFile = pchInclude.toLocalFile().toUtf8(); smartArgs << pchFile; clangArguments << pchFile.constData(); } + + if (needGccCompatibility(environment)) { + const auto compatFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevclangsupport/gcc_compat.h")).toUtf8(); + smartArgs << compatFile; + clangArguments << "-include" << compatFile.constData(); + } + addIncludes(&clangArguments, &smartArgs, includes.system, "-isystem"); addIncludes(&clangArguments, &smartArgs, includes.project, "-I"); m_definesFile.open(); QTextStream definesStream(&m_definesFile); Q_ASSERT(m_definesFile.isWritable()); const auto& defines = environment.defines(); for (auto it = defines.begin(); it != defines.end(); ++it) { definesStream << QStringLiteral("#define ") << it.key() << ' ' << it.value() << '\n'; } definesStream.flush(); smartArgs << m_definesFile.fileName().toUtf8(); clangArguments << "-imacros" << smartArgs.last().constData(); QVector unsaved; //For PrecompiledHeader, we don't want unsaved contents (and contents.isEmpty()) if (!options.testFlag(PrecompiledHeader)) { unsaved = toClangApi(unsavedFiles); } const CXErrorCode code = clang_parseTranslationUnit2( index->index(), tuUrl.byteArray().constData(), clangArguments.constData(), clangArguments.size(), unsaved.data(), unsaved.size(), flags, &m_unit ); if (code != CXError_Success) { qWarning() << "clang_parseTranslationUnit2 return with error code" << code; } if (m_unit) { setUnit(m_unit); m_environment = environment; if (options.testFlag(PrecompiledHeader)) { clang_saveTranslationUnit(m_unit, (tuUrl.byteArray() + ".pch").constData(), CXSaveTranslationUnit_None); } } else { qWarning() << "Failed to parse translation unit:" << tuUrl; } } ParseSessionData::~ParseSessionData() { clang_disposeTranslationUnit(m_unit); } void ParseSessionData::setUnit(CXTranslationUnit unit) { Q_ASSERT(!m_unit || unit == m_unit); m_unit = unit; const ClangString unitFile(clang_getTranslationUnitSpelling(unit)); m_file = clang_getFile(m_unit, unitFile.c_str()); } ClangParsingEnvironment ParseSessionData::environment() const { return m_environment; } ParseSession::ParseSession(const ParseSessionData::Ptr& data) : d(data) { if (d) { ENSURE_CHAIN_NOT_LOCKED d->m_mutex.lock(); } } ParseSession::~ParseSession() { if (d) { d->m_mutex.unlock(); } } void ParseSession::setData(const ParseSessionData::Ptr& data) { if (data == d) { return; } if (d) { d->m_mutex.unlock(); } d = data; if (d) { ENSURE_CHAIN_NOT_LOCKED d->m_mutex.lock(); } } ParseSessionData::Ptr ParseSession::data() const { return d; } IndexedString ParseSession::languageString() { static const IndexedString lang("Clang"); return lang; } QList ParseSession::problemsForFile(CXFile file) const { if (!d) { return {}; } QList problems; // extra clang diagnostics const uint numDiagnostics = clang_getNumDiagnostics(d->m_unit); problems.reserve(numDiagnostics); for (uint i = 0; i < numDiagnostics; ++i) { auto diagnostic = clang_getDiagnostic(d->m_unit, i); CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); CXFile diagnosticFile; clang_getFileLocation(location, &diagnosticFile, nullptr, nullptr, nullptr); if (diagnosticFile != file) { continue; } ProblemPointer problem(ClangDiagnosticEvaluator::createProblem(diagnostic, d->m_unit)); problems << problem; clang_disposeDiagnostic(diagnostic); } // other problem sources TodoExtractor extractor(unit(), file); problems << extractor.problems(); #if CINDEX_VERSION_MINOR > 30 // note that the below warning is triggered on every reparse when there is a precompiled preamble // see also TestDUChain::testReparseIncludeGuard const QString path = QDir(ClangString(clang_getFileName(file)).toString()).canonicalPath(); const IndexedString indexedPath(path); if (ClangHelpers::isHeader(path) && !clang_isFileMultipleIncludeGuarded(unit(), file) && !clang_Location_isInSystemHeader(clang_getLocationForOffset(d->m_unit, file, 0))) { ProblemPointer problem(new Problem); problem->setSeverity(IProblem::Warning); problem->setDescription(i18n("Header is not guarded against multiple inclusions")); problem->setExplanation(i18n("The given header is not guarded against multiple inclusions, " "either with the conventional #ifndef/#define/#endif macro guards or with #pragma once.")); problem->setFinalLocation({indexedPath, KTextEditor::Range()}); problem->setSource(IProblem::Preprocessor); problems << problem; // TODO: Easy to add an assistant here that adds the guards -- any takers? } #endif return problems; } CXTranslationUnit ParseSession::unit() const { return d ? d->m_unit : nullptr; } CXFile ParseSession::file(const QByteArray& path) const { return clang_getFile(unit(), path.constData()); } CXFile ParseSession::mainFile() const { return d ? d->m_file : nullptr; } bool ParseSession::reparse(const QVector& unsavedFiles, const ClangParsingEnvironment& environment) { if (!d || environment != d->m_environment) { return false; } auto unsaved = toClangApi(unsavedFiles); if (clang_reparseTranslationUnit(d->m_unit, unsaved.size(), unsaved.data(), clang_defaultReparseOptions(d->m_unit)) == 0) { d->setUnit(d->m_unit); return true; } else { return false; } } ClangParsingEnvironment ParseSession::environment() const { return d->m_environment; } diff --git a/languages/clang/tests/files/templates.cpp b/languages/clang/tests/files/templates.cpp index f1cc90a09c..61e6d3edd6 100644 --- a/languages/clang/tests/files/templates.cpp +++ b/languages/clang/tests/files/templates.cpp @@ -1,61 +1,54 @@ /// "toString" : "struct myTemplate", /// "kind" : "Type" template struct myTemplate {}; /// "toString" : "struct myTemplateChild", /// "kind" : "Type" struct myTemplateChild : myTemplate { }; // Test for CXType_DependentSizedArray template struct Class { /// "toString" : "char[] data" char data[10 * sizeof(T)]; }; template struct Class_volatile_const {}; template class TemplateTest {}; template class VariadicTemplate {}; -/// "type" : { "toString" : "TypeAliasTemplate", "EXPECT_FAIL": {"toString": "TypeAliasTemplateDecl is not accessible through LibClang"} } -template -using TypeAliasTemplate = T; - /// "type" : { "toString" : "Class_volatile_const< int >" } Class_volatile_const instance; -myTemplate& > templRefParam; -/// "type" : { "toString" : "myTemplate< myTemplate< int >& >", "EXPECT_FAIL": {"toString": "For some reasons reference gets lost. Need to investigate it further."} } -auto autoTemplRefParam = templRefParam; /// "type" : { "toString" : "VariadicTemplate< int, double, bool >", "EXPECT_FAIL": {"toString": "No way to get variadic template arguments with LibClang"} } VariadicTemplate variadic; /// "type" : { "toString" : "TemplateTest< const TemplateTest< int, 100 >, 30 >" } TemplateTest, 30> tst; template void test() { /// "type" : { "toString" : "const volatile auto" } const volatile auto type = Type(); } /*This example used to crash while building the type of Bar*/ /// "type" : { "toString" : "Bar" } template class Bar { /// "type" : { "toString" : "function void (int)" } void foo(UnknownType); /// "returnType" : { "toString" : "int" } UnknownType foo(); }; diff --git a/languages/clang/tests/test_codecompletion.cpp b/languages/clang/tests/test_codecompletion.cpp index 532c95c979..c7a2a265bc 100644 --- a/languages/clang/tests/test_codecompletion.cpp +++ b/languages/clang/tests/test_codecompletion.cpp @@ -1,1139 +1,1167 @@ /* * Copyright 2014 David Stevens * Copyright 2014 Kevin Funk * Copyright 2015 Milian Wolff * Copyright 2015 Sergey Kalinichev * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_codecompletion.h" #include #include #include #include "duchain/parsesession.h" #include "util/clangtypes.h" #include #include #include #include #include #include "codecompletion/completionhelper.h" #include "codecompletion/context.h" #include "codecompletion/includepathcompletioncontext.h" #include "../clangsettings/clangsettingsmanager.h" #include #include #include #include #if KTEXTEDITOR_VERSION < QT_VERSION_CHECK(5, 10, 0) Q_DECLARE_METATYPE(KTextEditor::Cursor); #endif QTEST_MAIN(TestCodeCompletion); static const auto NoMacroOrBuiltin = ClangCodeCompletionContext::ContextFilters( ClangCodeCompletionContext::NoBuiltins | ClangCodeCompletionContext::NoMacros); using namespace KDevelop; using ClangCodeCompletionItemTester = CodeCompletionItemTester; struct CompletionItems { CompletionItems(){} CompletionItems(const KTextEditor::Cursor& position, const QStringList& completions, const QStringList& declarationItems = {}) : position(position) , completions(completions) , declarationItems(declarationItems) {}; KTextEditor::Cursor position; QStringList completions; QStringList declarationItems; ///< completion items that have associated declarations. Declarations with higher match quality at the top. @sa KTextEditor::CodeCompletionModel::MatchQuality }; Q_DECLARE_TYPEINFO(CompletionItems, Q_MOVABLE_TYPE); Q_DECLARE_METATYPE(CompletionItems); struct CompletionPriorityItem { CompletionPriorityItem(const QString name, int matchQuality = 0, int inheritanceDepth = 0, const QString failMessage = {}) : name(name) , failMessage(failMessage) , matchQuality(matchQuality) , inheritanceDepth(inheritanceDepth) {} QString name; QString failMessage; int matchQuality; int inheritanceDepth; }; struct CompletionPriorityItems : public CompletionItems { CompletionPriorityItems(){} CompletionPriorityItems(const KTextEditor::Cursor& position, const QList& completions) : CompletionItems(position, {}) , completions(completions) {}; QList completions; }; Q_DECLARE_TYPEINFO(CompletionPriorityItems, Q_MOVABLE_TYPE); Q_DECLARE_METATYPE(CompletionPriorityItems); void TestCodeCompletion::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevelop.plugins.clang.debug=true\n")); QVERIFY(qputenv("KDEV_DISABLE_PLUGINS", "kdevcppsupport")); AutoTestShell::init({QStringLiteral("kdevclangsupport")}); TestCore::initialize(); ClangSettingsManager::self()->m_enableTesting = true; } void TestCodeCompletion::cleanupTestCase() { TestCore::shutdown(); } namespace { struct NoopTestFunction { void operator()(const ClangCodeCompletionItemTester& /*tester*/) const { } }; template void executeCompletionTest(const ReferencedTopDUContext& top, const CompletionItems& expectedCompletionItems, const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin, CustomTestFunction customTestFunction = {}) { DUChainReadLocker lock; const ParseSessionData::Ptr sessionData(dynamic_cast(top->ast().data())); QVERIFY(sessionData); lock.unlock(); // TODO: We should not need to pass 'session' to the context, should just use the base class ctor auto context = new ClangCodeCompletionContext(DUContextPointer(top), sessionData, top->url().toUrl(), expectedCompletionItems.position, QString()); context->setFilters(filters); lock.lock(); auto tester = ClangCodeCompletionItemTester(QExplicitlySharedDataPointer(context)); int previousMatchQuality = 10; for(const auto& declarationName : expectedCompletionItems.declarationItems){ const auto declarationItem = tester.findItem(declarationName); QVERIFY(declarationItem); QVERIFY(declarationItem->declaration()); auto matchQuality = tester.itemData(declarationItem, KTextEditor::CodeCompletionModel::Name, KTextEditor::CodeCompletionModel::MatchQuality).toInt(); QVERIFY(matchQuality <= previousMatchQuality); previousMatchQuality = matchQuality; } tester.names.sort(); QEXPECT_FAIL("look-ahead function primary type argument", "No API in LibClang to determine expected code completion type", Continue); QEXPECT_FAIL("look-ahead template parameter substitution", "No parameters substitution so far", Continue); QEXPECT_FAIL("look-ahead auto item", "Auto type, like many other types, is not exposed through LibClang. We assign DelayedType to it instead of IdentifiedType", Continue); if (tester.names.size() != expectedCompletionItems.completions.size()) { qDebug() << "different results:\nactual:" << tester.names << "\nexpected:" << expectedCompletionItems.completions; } QCOMPARE(tester.names, expectedCompletionItems.completions); customTestFunction(tester); } template void executeCompletionTest(const QString& code, const CompletionItems& expectedCompletionItems, const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin, CustomTestFunction customTestFunction = {}) { TestFile file(code, "cpp"); QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)); executeCompletionTest(file.topContext(), expectedCompletionItems, filters, customTestFunction); } void executeCompletionPriorityTest(const QString& code, const CompletionPriorityItems& expectedCompletionItems, const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin) { TestFile file(code, "cpp"); QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); const ParseSessionData::Ptr sessionData(dynamic_cast(top->ast().data())); QVERIFY(sessionData); DUContextPointer topPtr(top); // don't hold DUChain lock when constructing ClangCodeCompletionContext lock.unlock(); auto context = new ClangCodeCompletionContext(topPtr, sessionData, file.url().toUrl(), expectedCompletionItems.position, QString()); context->setFilters(filters); lock.lock(); auto tester = ClangCodeCompletionItemTester(QExplicitlySharedDataPointer(context)); for(const auto& declaration : expectedCompletionItems.completions){ const auto declarationItem = tester.findItem(declaration.name); QVERIFY(declarationItem); QVERIFY(declarationItem->declaration()); auto matchQuality = tester.itemData(declarationItem, KTextEditor::CodeCompletionModel::Name, KTextEditor::CodeCompletionModel::MatchQuality).toInt(); auto inheritanceDepth = declarationItem->inheritanceDepth(); if(!declaration.failMessage.isEmpty()){ QEXPECT_FAIL("", declaration.failMessage.toUtf8().constData(), Continue); } QVERIFY(matchQuality == declaration.matchQuality && inheritanceDepth == declaration.inheritanceDepth); } } void executeMemberAccessReplacerTest(const QString& code, const CompletionItems& expectedCompletionItems, const ClangCodeCompletionContext::ContextFilters& filters = NoMacroOrBuiltin) { TestFile file(code, "cpp"); auto document = ICore::self()->documentController()->openDocument(file.url().toUrl()); QVERIFY(document); ICore::self()->documentController()->activateDocument(document); auto view = ICore::self()->documentController()->activeTextDocumentView(); Q_ASSERT(view); view->setCursorPosition(expectedCompletionItems.position); QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); const ParseSessionData::Ptr sessionData(dynamic_cast(top->ast().data())); QVERIFY(sessionData); DUContextPointer topPtr(top); lock.unlock(); auto context = new ClangCodeCompletionContext(topPtr, sessionData, file.url().toUrl(), expectedCompletionItems.position, code); QApplication::processEvents(); document->close(KDevelop::IDocument::Silent); // The previous ClangCodeCompletionContext call should replace member access. context = new ClangCodeCompletionContext(topPtr, sessionData, file.url().toUrl(), expectedCompletionItems.position, QString()); context->setFilters(filters); lock.lock(); auto tester = ClangCodeCompletionItemTester(QExplicitlySharedDataPointer(context)); tester.names.sort(); QCOMPARE(tester.names, expectedCompletionItems.completions); } using IncludeTester = CodeCompletionItemTester; QExplicitlySharedDataPointer executeIncludePathCompletion(TestFile* file, const KTextEditor::Cursor& position) { if (!file->parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)) { QTest::qFail("Failed to parse source file.", __FILE__, __LINE__); return {}; } DUChainReadLocker lock; auto top = file->topContext(); if (!top) { QTest::qFail("Failed to parse source file.", __FILE__, __LINE__); return {}; } const ParseSessionData::Ptr sessionData(dynamic_cast(top->ast().data())); if (!sessionData) { QTest::qFail("Failed to acquire parse session data.", __FILE__, __LINE__); return {}; } DUContextPointer topPtr(top); lock.unlock(); auto text = file->fileContents(); int textLength = -1; if (position.isValid()) { textLength = 0; for (int i = 0; i < position.line(); ++i) { textLength = text.indexOf('\n', textLength) + 1; } textLength += position.column(); } auto context = new IncludePathCompletionContext(topPtr, sessionData, file->url().toUrl(), position, text.mid(0, textLength)); return QExplicitlySharedDataPointer{context}; } } void TestCodeCompletion::testClangCodeCompletion() { QFETCH(QString, code); QFETCH(CompletionItems, expectedItems); executeCompletionTest(code, expectedItems); } void TestCodeCompletion::testClangCodeCompletion_data() { QTest::addColumn("code"); QTest::addColumn("expectedItems"); QTest::newRow("assignment") << "int foo = 5; \nint bar = " << CompletionItems{{1,9}, { "bar", "foo", }, {"bar","foo"}}; QTest::newRow("dotmemberaccess") << "class Foo { public: void foo() {} bool operator=(Foo &&) }; int main() { Foo f; \nf. " << CompletionItems{{1, 2}, { "foo", "operator=" }, {"foo", "operator="}}; QTest::newRow("arrowmemberaccess") << "class Foo { public: void foo() {} }; int main() { Foo* f = new Foo; \nf-> }" << CompletionItems{{1, 3}, { "foo" }, {"foo"}}; QTest::newRow("enum-case") << "enum Foo { foo, bar }; int main() { Foo f; switch (f) {\ncase " << CompletionItems{{1,4}, { "bar", "foo", }, {"foo", "bar"}}; QTest::newRow("only-private") << "class SomeStruct { private: void priv() {} };\n" "int main() { SomeStruct s;\ns. " << CompletionItems{{2, 2}, { }}; QTest::newRow("private-friend") << "class SomeStruct { private: void priv() {} friend int main(); };\n" "int main() { SomeStruct s;\ns. " << CompletionItems{{2, 2}, { "priv", }, {"priv"}}; QTest::newRow("private-public") << "class SomeStruct { public: void pub() {} private: void priv() {} };\n" "int main() { SomeStruct s;\ns. " << CompletionItems{{2, 2}, { "pub", }, {"pub"}}; QTest::newRow("protected-public") << "class SomeStruct { public: void pub() {} protected: void prot() {} };\n" "int main() { SomeStruct s;\ns. " << CompletionItems{{2, 2}, { "pub", }, {"pub"}}; QTest::newRow("localVariable") << "int main() { int localVariable;\nloc " << CompletionItems{{1, 3}, {"localVariable","main"}, {"localVariable", "main"} }; QTest::newRow("globalVariable") << "int globalVariable;\nint main() { \ngl " << CompletionItems{{2, 2}, {"globalVariable","main"}, {"globalVariable", "main"} }; QTest::newRow("namespaceVariable") << "namespace NameSpace{int variable};\nint main() { \nNameSpace:: " << CompletionItems{{2, 11}, {"variable"}, {"variable"} }; QTest::newRow("parentVariable") << "class A{public: int m_variable;};class B : public A{};\nint main() { B b;\nb. " << CompletionItems{{2, 2}, {"m_variable"}, {"m_variable"} }; QTest::newRow("itemsPriority") << "class A; class B; void f(A); int main(){ A c; B b;f(\n} " << CompletionItems{{1, 0}, {"A", "B", "b", "c", "f", #if CINDEX_VERSION_MINOR >= 30 "f", #endif "main"}, {"c", "A", "b", "B"} }; QTest::newRow("function-arguments") << "class Abc; int f(Abc){\n " << CompletionItems{{1, 0}, { "Abc", "f", }}; QTest::newRow("look-ahead int") << "struct LookAhead { int intItem;}; int main() {LookAhead* pInstance; LookAhead instance; int i =\n }" << CompletionItems{{1, 0}, { "LookAhead", "i", "instance", "instance.intItem", "main", "pInstance", "pInstance->intItem", }}; QTest::newRow("look-ahead class") << "class Class{}; struct LookAhead {Class classItem;}; int main() {LookAhead* pInstance; LookAhead instance; Class cl =\n }" << CompletionItems{{1, 0}, { "Class", "LookAhead", "cl", "instance", "instance.classItem", "main", "pInstance", "pInstance->classItem", }}; QTest::newRow("look-ahead function argument") << "class Class{}; struct LookAhead {Class classItem;}; void function(Class cl);" "int main() {LookAhead* pInstance; LookAhead instance; function(\n }" << CompletionItems{{1, 0}, { "Class", "LookAhead", "function", #if CINDEX_VERSION_MINOR >= 30 "function", #endif "instance", "instance.classItem", "main", "pInstance", "pInstance->classItem", }}; QTest::newRow("look-ahead function primary type argument") << "struct LookAhead {double doubleItem;}; void function(double double);" "int main() {LookAhead* pInstance; LookAhead instance; function(\n }" << CompletionItems{{1, 0}, { "LookAhead", "function", "instance", "instance.doubleItem", "main", "pInstance", "pInstance->doubleItem", }}; QTest::newRow("look-ahead typedef") << "typedef double DOUBLE; struct LookAhead {DOUBLE doubleItem;};" "int main() {LookAhead* pInstance; LookAhead instance; double i =\n " << CompletionItems{{1, 0}, { "DOUBLE", "LookAhead", "i", "instance", "instance.doubleItem", "main", "pInstance", "pInstance->doubleItem", }}; QTest::newRow("look-ahead pointer") << "struct LookAhead {int* pInt;};" "int main() {LookAhead* pInstance; LookAhead instance; int* i =\n " << CompletionItems{{1, 0}, { "LookAhead", "i", "instance", "instance.pInt", "main", "pInstance", "pInstance->pInt", }}; QTest::newRow("look-ahead template") << "template struct LookAhead {int intItem;};" "int main() {LookAhead* pInstance; LookAhead instance; int i =\n " << CompletionItems{{1, 0}, { "LookAhead", "i", "instance", "instance.intItem", "main", "pInstance", "pInstance->intItem", }}; QTest::newRow("look-ahead template parameter substitution") << "template struct LookAhead {T itemT;};" "int main() {LookAhead* pInstance; LookAhead instance; int i =\n " << CompletionItems{{1, 0}, { "LookAhead", "i", "instance", "instance.itemT", "main", "pInstance", "pInstance->itemT", }}; QTest::newRow("look-ahead item access") << "class Class { public: int publicInt; protected: int protectedInt; private: int privateInt;};" "int main() {Class cl; int i =\n " << CompletionItems{{1, 0}, { "Class", "cl", "cl.publicInt", "i", "main", }}; QTest::newRow("look-ahead auto item") << "struct LookAhead { int intItem; };" "int main() {auto instance = LookAhead(); int i = \n " << CompletionItems{{1, 0}, { "LookAhead", "i", "instance", "instance.intItem" }}; } void TestCodeCompletion::testReplaceMemberAccess() { QFETCH(QString, code); QFETCH(CompletionItems, expectedItems); executeMemberAccessReplacerTest(code, expectedItems); } void TestCodeCompletion::testReplaceMemberAccess_data() { QTest::addColumn("code"); QTest::addColumn("expectedItems"); QTest::newRow("replace arrow to dot") << "struct Struct { void function(); };" "int main() { Struct s; \ns-> " << CompletionItems{{1, 3}, { "function" }}; QTest::newRow("replace dot to arrow") << "struct Struct { void function(); };" "int main() { Struct* s; \ns. " << CompletionItems{{1, 3}, { "function" }}; QTest::newRow("no replacement needed") << "int main() { double a = \n0. " << CompletionItems{{1, 2}, { }}; } void TestCodeCompletion::testVirtualOverride() { QFETCH(QString, code); QFETCH(CompletionItems, expectedItems); executeCompletionTest(code, expectedItems, ClangCodeCompletionContext::NoClangCompletion); } void TestCodeCompletion::testVirtualOverride_data() { QTest::addColumn("code"); QTest::addColumn("expectedItems"); QTest::newRow("basic") << "class Foo { virtual void foo(); virtual void foo(char c); virtual char foo(char c, int i, double d); };\n" "class Bar : Foo \n{void foo(char c) override;\n}" << CompletionItems{{3, 1}, {"foo()", "foo(char c, int i, double d)"}}; QTest::newRow("template") << "template class Foo { virtual T2 foo(T1 a, T2 b, int i); virtual T2 overridden(T1 a); } ;\n" "class Bar : Foo \n{double overridden(char a) override;\n}" << CompletionItems{{3, 1}, {"foo(char a, double b, int i)"}}; QTest::newRow("nested-template") << "template class Foo { virtual T2 foo(T1 a, T2 b, int i); virtual T2 overridden(T1 a, T2 b, int i); } ;\n" "template class Baz { };\n" "class Bar : Foo> \n{Baz overridden(char a, Baz b, int i) override;\n}" << CompletionItems{{4, 1}, {"foo(char a, Baz b, int i)"}}; QTest::newRow("multi") << "class Foo { virtual int foo(int i); virtual int overridden(int i); };\n" "class Baz { virtual char baz(char c); };\n" "class Bar : Foo, Baz \n{int overridden(int i) override;\n}" << CompletionItems{{4, 1}, {"baz(char c)", "foo(int i)"}}; QTest::newRow("deep") << "class Foo { virtual int foo(int i); virtual int overridden(int i); };\n" "class Baz : Foo { };\n" "class Bar : Baz \n{int overridden(int i) overriden;\n}" << CompletionItems{{4, 1}, {"foo(int i)"}}; QTest::newRow("pure") << "class Foo { virtual void foo() = 0; virtual void overridden() = 0;};\n" "class Bar : Foo \n{void overridden() override;\n};" << CompletionItems{{3, 0}, {"foo() = 0"}}; QTest::newRow("const") << "class Foo { virtual void foo(const int b) const; virtual void overridden(const int b) const; }\n;" "class Bar : Foo \n{void overridden(const int b) const override;\n}" << CompletionItems{{3, 1}, {"foo(const int b) const"}}; } void TestCodeCompletion::testImplement() { QFETCH(QString, code); QFETCH(CompletionItems, expectedItems); executeCompletionTest(code, expectedItems, ClangCodeCompletionContext::NoClangCompletion); } void TestCodeCompletion::testImplement_data() { QTest::addColumn("code"); QTest::addColumn("expectedItems"); QTest::newRow("basic") << "int foo(char c, int i); \n" << CompletionItems{{1, 0}, {"foo(char c, int i)"}}; QTest::newRow("class") << "class Foo { \n" "int bar(char c, int i); \n\n" "}; \n" << CompletionItems{{2, 0}, {}}; QTest::newRow("class2") << "class Foo { \n" "int bar(char c, int i); \n\n" "}; \n" << CompletionItems{{4, 0}, {"Foo::bar(char c, int i)"}}; QTest::newRow("namespace") << "namespace Foo { \n" "int bar(char c, int i); \n\n" "}; \n" << CompletionItems{{2, 0}, {"bar(char c, int i)"}}; + QTest::newRow("anonymous-namespace") + << R"( + namespace { + int bar(char c, int i); + }; + )" + << CompletionItems{{3, 0}, {"bar(char c, int i)"}}; + + QTest::newRow("anonymous-namespace2") + << R"( + namespace { + int bar(char c, int i); + }; + )" + << CompletionItems{{4, 0}, {}}; + QTest::newRow("namespace2") << "namespace Foo { \n" "int bar(char c, int i); \n\n" "}; \n" << CompletionItems{{4, 0}, {"Foo::bar(char c, int i)"}}; QTest::newRow("two-namespace") << "namespace Foo { int bar(char c, int i); };\n" "namespace Foo {\n" "};\n" << CompletionItems{{2, 0}, {"bar(char c, int i)"}}; QTest::newRow("destructor") << "class Foo { ~Foo(); }\n" << CompletionItems{{1, 0}, {"Foo::~Foo()"}}; QTest::newRow("constructor") << "class Foo { \n" "Foo(int i); \n" "}; \n" << CompletionItems{{3, 1}, {"Foo::Foo(int i)"}}; QTest::newRow("template") << "template class Foo { T bar(T t); };\n" << CompletionItems{{1, 1}, {"Foo::bar(T t)"}}; QTest::newRow("specialized-template") << "template class Foo { \n" "T bar(T t); \n" "}; \n" "template T Foo::bar(T t){} \n" "template<> class Foo { \n" "int bar(int t); \n" "}\n" << CompletionItems{{6, 1}, {"Foo::bar(int t)"}}; QTest::newRow("nested-class") << "class Foo { \n" "class Bar { \n" "int baz(char c, int i); \n\n" "}; \n\n" "}; \n\n" << CompletionItems {{3, 1}, {}}; QTest::newRow("nested-class2") << "class Foo { \n" "class Bar { \n" "int baz(char c, int i); \n\n" "}; \n\n" "}; \n\n" << CompletionItems {{5, 1}, {}}; QTest::newRow("nested-class3") << "class Foo { \n" "class Bar { \n" "int baz(char c, int i); \n\n" "}; \n\n" "}; \n\n" << CompletionItems {{7, 1}, {"Foo::Bar::baz(char c, int i)"}}; QTest::newRow("nested-namespace") << "namespace Foo { \n" "namespace Bar { \n" "int baz(char c, int i); \n\n" "}; \n\n" "}; \n\n" << CompletionItems {{3, 1}, {"baz(char c, int i)"}}; QTest::newRow("nested-namespace2") << "namespace Foo { \n" "namespace Bar { \n" "int baz(char c, int i); \n\n" "}; \n\n" "}; \n\n" << CompletionItems {{5, 1}, {"Bar::baz(char c, int i)"}}; QTest::newRow("nested-namespace3") << "namespace Foo { \n" "namespace Bar { \n" "int baz(char c, int i); \n\n" "}; \n\n" "}; \n\n" << CompletionItems {{7, 1}, {"Foo::Bar::baz(char c, int i)"}}; QTest::newRow("partial-template") << "template class Foo { \n" "template class Bar;\n" "template class Bar { void baz(T t, U u); }\n" "}\n" << CompletionItems{{5,1}, {"Foo::Bar::baz(T t, U u)"}}; QTest::newRow("variadic") << "int foo(...); int bar(int i, ...); \n" << CompletionItems{{1, 1}, {"bar(int i, ...)", "foo(...)"}}; QTest::newRow("const") << "class Foo { int bar() const; };" << CompletionItems{{3, 1}, {"Foo::bar() const"}}; QTest::newRow("multiple-methods") << "class Foo { int bar(); void foo(); char asdf() const; };" << CompletionItems{{1, 1}, {"Foo::asdf() const", "Foo::bar()", "Foo::foo()"}}; // explicitly deleted/defaulted functions should not appear in the implements completion QTest::newRow("deleted-copy-ctor") << "struct S { S(); S(const S&) = /*some comment*/ delete; };" << CompletionItems{{1,1}, {"S::S()"}}; QTest::newRow("deleted-overload-member") << "struct Foo {\n" " int x();\n" " int x(int) =delete;\n" "};\n" << CompletionItems{{5,1}, {"Foo::x()"}}; QTest::newRow("deleted-overload-global") << "int x();\n" "int x(int)= delete;\n" << CompletionItems{{2,1}, {"x()"}}; // FIXME: the rage seems to be wrong here (stops after =), but I don't know how to fix that // QDEBUG : TestCodeCompletion::testImplement(deleted-overload-global) default: "int x(int)=" contains "=\\s*delete\\s*;" = false QTest::newRow("defaulted-copy-ctor") << "struct S { S(); S(const S&) = default; };" << CompletionItems{{1,1}, {"S::S()"}}; QTest::newRow("defaulted-dtor") << "struct Foo {\n" " Foo();\n" " ~Foo() =default;\n" "};\n" << CompletionItems{{5,1}, {"Foo::Foo()"}}; QTest::newRow("bug355163") << R"( #include namespace test { template struct IsSafeConversion : public std::is_same::type> { }; } // namespace test )" << CompletionItems{{7,0}, {}}; + + QTest::newRow("bug355954") + << R"( + struct Hello { + struct Private; + }; + + struct Hello::Private { + void test(); + }; + )" + << CompletionItems{{8,0}, {"Hello::Private::test()"}}; } void TestCodeCompletion::testImplementOtherFile() { TestFile header1("void foo();", "h"); QVERIFY(header1.parseAndWait()); TestFile header2("void bar();", "h"); QVERIFY(header2.parseAndWait()); TestFile impl(QString("#include \"%1\"\n" "#include \"%2\"\n" "void asdf();\n\n") .arg(header1.url().str()) .arg(header2.url().str()), "cpp", &header1); CompletionItems expectedItems{{3,1}, {"asdf()", "foo()"}}; QVERIFY(impl.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)); executeCompletionTest(impl.topContext(), expectedItems); } void TestCodeCompletion::testInvalidCompletions() { QFETCH(QString, code); QFETCH(CompletionItems, expectedItems); executeCompletionTest(code, expectedItems); } void TestCodeCompletion::testInvalidCompletions_data() { QTest::addColumn("code"); QTest::addColumn("expectedItems"); QTest::newRow("invalid-context-incomment") << "class Foo { int bar() const; };\n/*\n*/" << CompletionItems{{2, 0}, {}}; } void TestCodeCompletion::testIncludePathCompletion_data() { QTest::addColumn("code"); QTest::addColumn("cursor"); QTest::addColumn("itemId"); QTest::addColumn("result"); QTest::newRow("global-1") << QString("#include ") << KTextEditor::Cursor(0, 9) << QString("iostream") << QString("#include "); QTest::newRow("global-2") << QString("#include <") << KTextEditor::Cursor(0, 9) << QString("iostream") << QString("#include "); QTest::newRow("global-3") << QString("#include <") << KTextEditor::Cursor(0, 10) << QString("iostream") << QString("#include "); QTest::newRow("global-4") << QString("# include <") << KTextEditor::Cursor(0, 12) << QString("iostream") << QString("# include "); QTest::newRow("global-5") << QString("# include <") << KTextEditor::Cursor(0, 14) << QString("iostream") << QString("# include "); QTest::newRow("global-6") << QString("# include <> /* 1 */") << KTextEditor::Cursor(0, 14) << QString("iostream") << QString("# include /* 1 */"); QTest::newRow("global-7") << QString("# include /* 1 */ <> /* 1 */") << KTextEditor::Cursor(0, 21) << QString("iostream") << QString("# include /* 1 */ /* 1 */"); QTest::newRow("global-8") << QString("# /* 1 */ include /* 1 */ <> /* 1 */") << KTextEditor::Cursor(0, 28) << QString("iostream") << QString("# /* 1 */ include /* 1 */ /* 1 */"); QTest::newRow("global-9") << QString("#include ") << KTextEditor::Cursor(0, 10) << QString("iostream") << QString("#include "); QTest::newRow("global-10") << QString("#include ") << KTextEditor::Cursor(0, 14) << QString("cstdint") << QString("#include "); QTest::newRow("global-11") << QString("#include ") << KTextEditor::Cursor(0, 17) << QString("cstdint") << QString("#include "); QTest::newRow("local-0") << QString("#include \"") << KTextEditor::Cursor(0, 10) << QString("foo/") << QString("#include \"foo/\""); QTest::newRow("local-1") << QString("#include \"foo/\"") << KTextEditor::Cursor(0, 14) << QString("bar/") << QString("#include \"foo/bar/\""); QTest::newRow("local-2") << QString("#include \"foo/") << KTextEditor::Cursor(0, 14) << QString("bar/") << QString("#include \"foo/bar/\""); QTest::newRow("local-3") << QString("# /* 1 */ include /* 1 */ \"\" /* 1 */") << KTextEditor::Cursor(0, 28) << QString("foo/") << QString("# /* 1 */ include /* 1 */ \"foo/\" /* 1 */"); QTest::newRow("local-4") << QString("# /* 1 */ include /* 1 */ \"foo/\" /* 1 */") << KTextEditor::Cursor(0, 31) << QString("bar/") << QString("# /* 1 */ include /* 1 */ \"foo/bar/\" /* 1 */"); QTest::newRow("local-5") << QString("#include \"foo/\"") << KTextEditor::Cursor(0, 10) << QString("foo/") << QString("#include \"foo/\""); QTest::newRow("local-6") << QString("#include \"foo/asdf\"") << KTextEditor::Cursor(0, 10) << QString("foo/") << QString("#include \"foo/\""); QTest::newRow("local-7") << QString("#include \"foo/asdf\"") << KTextEditor::Cursor(0, 14) << QString("bar/") << QString("#include \"foo/bar/\""); } struct DeleteDocument { void operator()(KTextEditor::View* view) const { delete view->document(); } }; static std::unique_ptr createView(const QUrl& url, QObject* parent) { KTextEditor::Editor* editor = KTextEditor::Editor::instance(); Q_ASSERT(editor); auto doc = editor->createDocument(parent); Q_ASSERT(doc); bool opened = doc->openUrl(url); Q_ASSERT(opened); Q_UNUSED(opened); auto view = doc->createView(nullptr); Q_ASSERT(view); return std::unique_ptr(view); } void TestCodeCompletion::testIncludePathCompletion() { QFETCH(QString, code); QFETCH(KTextEditor::Cursor, cursor); QFETCH(QString, itemId); QFETCH(QString, result); QTemporaryDir tempDir; QDir dir(tempDir.path()); QVERIFY(dir.mkpath("foo/bar/asdf")); TestFile file(code, "cpp", 0, tempDir.path()); IncludeTester tester(executeIncludePathCompletion(&file, cursor)); QVERIFY(tester.completionContext); QVERIFY(tester.completionContext->isValid()); auto item = tester.findItem(itemId); QVERIFY(item); auto view = createView(file.url().toUrl(), this); qDebug() << view.get(); QVERIFY(view.get()); auto doc = view->document(); item->execute(view.get(), KTextEditor::Range(cursor, cursor)); QCOMPARE(doc->text(), result); const auto newCursor = view->cursorPosition(); QCOMPARE(newCursor.line(), cursor.line()); if (!itemId.endsWith('/')) { // file inserted, cursor should be at end of line QCOMPARE(newCursor.column(), doc->lineLength(cursor.line())); } else { // directory inserted, cursor should be before the " or > const auto cursorChar = doc->characterAt(newCursor); QVERIFY(cursorChar == '"' || cursorChar == '>'); } } void TestCodeCompletion::testIncludePathCompletionLocal() { TestFile header("int foo() { return 42; }\n", "h"); TestFile impl("#include \"", "cpp", &header); IncludeTester tester(executeIncludePathCompletion(&impl, {0, 10})); QVERIFY(tester.names.contains(header.url().toUrl().fileName())); QVERIFY(!tester.names.contains("iostream")); } void TestCodeCompletion::testOverloadedFunctions() { TestFile file("void f(); int f(int); void f(int, double){\n ", "cpp"); QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); const ParseSessionData::Ptr sessionData(dynamic_cast(top->ast().data())); QVERIFY(sessionData); DUContextPointer topPtr(top); lock.unlock(); const auto context = new ClangCodeCompletionContext(topPtr, sessionData, file.url().toUrl(), {1, 0}, QString()); context->setFilters(NoMacroOrBuiltin); lock.lock(); const auto tester = ClangCodeCompletionItemTester(QExplicitlySharedDataPointer(context)); QCOMPARE(tester.items.size(), 3); for (const auto& item : tester.items) { auto function = item->declaration()->type(); const QString display = item->declaration()->identifier().toString() + function->partToString(FunctionType::SignatureArguments); const QString itemDisplay = tester.itemData(item).toString() + tester.itemData(item, KTextEditor:: CodeCompletionModel::Arguments).toString(); QCOMPARE(display, itemDisplay); } QVERIFY(tester.items[0]->declaration().data() != tester.items[1]->declaration().data()); QVERIFY(tester.items[0]->declaration().data() != tester.items[2]->declaration().data()); QVERIFY(tester.items[1]->declaration().data() != tester.items[2]->declaration().data()); } void TestCodeCompletion::testCompletionPriority() { QFETCH(QString, code); QFETCH(CompletionPriorityItems, expectedItems); executeCompletionPriorityTest(code, expectedItems); } void TestCodeCompletion::testCompletionPriority_data() { QTest::addColumn("code"); QTest::addColumn("expectedItems"); QTest::newRow("pointer") << "class A{}; class B{}; class C : public B{}; int main(){A* a; B* b; C* c; b =\n " << CompletionPriorityItems{{1,0}, {{"a", 0, 21}, {"b", 9, 0}, {"c", 8, 0, QStringLiteral("Pointer to derived class is not added to the Best Matches group")}}}; QTest::newRow("class") << "class A{}; class B{}; class C : public B{}; int main(){A a; B b; C c; b =\n " << CompletionPriorityItems{{1,0}, {{"a", 0, 21}, {"b", 9, 0}, {"c", 8, 0, QStringLiteral("Derived class is not added to the Best Matches group")}}}; QTest::newRow("primary-types") << "class A{}; int main(){A a; int b; bool c = \n " << CompletionPriorityItems{{1,0}, {{"a", 0, 34}, {"b", 8, 0}, {"c", 9, 0}}}; QTest::newRow("reference") << "class A{}; class B{}; class C : public B{};" "int main(){A tmp; A& a = tmp; C tmp2; C& c = tmp2; B& b =\n ;}" << CompletionPriorityItems{{1,0}, {{"a", 0, 21}, {"b", 9, 0}, {"c", 8, 0, QStringLiteral("Reference to derived class is not added to the Best Matches group")}}}; QTest::newRow("typedef") << "struct A{}; struct B{}; typedef A AA; typedef B BB; void f(A p);" "int main(){ BB b; AA a; f(\n }" << CompletionPriorityItems{{1,0}, {{"a", 9, 0}, {"b", 0, 21}}}; QTest::newRow("returnType") << "struct A{}; struct B{}; struct Test{A f();B g(); Test() { A a =\n }};" << CompletionPriorityItems{{1,0}, {{"f", 9, 0}, {"g", 0, 21}}}; QTest::newRow("template") << "template class Class{}; template class Class2{};" "int main(){ Class a; Class2 b =\n }" << CompletionPriorityItems{{1,0}, {{"b", 9, 0}, {"a", 0, 21}}}; QTest::newRow("protected-access") << "class Base { protected: int m_protected; };" "class Derived: public Base {public: void g(){\n }};" << CompletionPriorityItems{{1,0}, {{"m_protected", 0, 37}}}; QTest::newRow("protected-access2") << "class Base { protected: int m_protected; };" "class Derived: public Base {public: void f();};" "void Derived::f(){\n }" << CompletionPriorityItems{{1,0}, {{"m_protected", 0, 37}}}; } void TestCodeCompletion::testVariableScope() { TestFile file("int var; \nvoid test(int var) {int tmp =\n }", "cpp"); QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); const ParseSessionData::Ptr sessionData(dynamic_cast(top->ast().data())); QVERIFY(sessionData); DUContextPointer topPtr(top); lock.unlock(); const auto context = new ClangCodeCompletionContext(topPtr, sessionData, file.url().toUrl(), {2, 0}, QString()); context->setFilters(NoMacroOrBuiltin); lock.lock(); const auto tester = ClangCodeCompletionItemTester(QExplicitlySharedDataPointer(context)); QCOMPARE(tester.items.size(), 4); auto item = tester.findItem(QStringLiteral("var")); VERIFY(item); QCOMPARE(item->declaration()->range().start, CursorInRevision(1, 14)); } void TestCodeCompletion::testArgumentHintCompletion() { QFETCH(QString, code); QFETCH(CompletionItems, expectedItems); executeCompletionTest(code, expectedItems); } void TestCodeCompletion::testArgumentHintCompletion_data() { #if CINDEX_VERSION_MINOR < 30 QSKIP("You need at least LibClang 3.7"); #endif QTest::addColumn("code"); QTest::addColumn("expectedItems"); QTest::newRow("global function") << "void foo(int);\n" "int main() { \nfoo( " << CompletionItems{{2,4}, { "foo", "foo", "main" }}; QTest::newRow("member function") << "struct Struct{ void foo(int);}\n" "int main() {Struct s; \ns.foo( " << CompletionItems{{2,6}, { "Struct", "foo", "main", "s" }}; QTest::newRow("template function") << "template void foo(T);\n" "int main() { \nfoo( " << CompletionItems{{2,6}, { "foo", "foo", "main" }}; QTest::newRow("overloaded functions") << "void foo(int); void foo(int, double)\n" "int main() { \nfoo( " << CompletionItems{{2,6}, { "foo", "foo", "foo", "foo", "main" }}; QTest::newRow("overloaded functions2") << "void foo(int); void foo(int, double)\n" "int main() { foo(1,\n " << CompletionItems{{2,1}, { "foo", "foo", "foo", "main" }}; } void TestCodeCompletion::testArgumentHintCompletionDefaultParameters() { #if CINDEX_VERSION_MINOR < 30 QSKIP("You need at least LibClang 3.7"); #endif TestFile file("void f(int i, int j = 0, double k =1){\nf( ", "cpp"); QVERIFY(file.parseAndWait(TopDUContext::AllDeclarationsContextsUsesAndAST)); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); const ParseSessionData::Ptr sessionData(dynamic_cast(top->ast().data())); QVERIFY(sessionData); DUContextPointer topPtr(top); lock.unlock(); const auto context = new ClangCodeCompletionContext(topPtr, sessionData, file.url().toUrl(), {1, 2}, QString()); context->setFilters(NoMacroOrBuiltin); lock.lock(); const auto tester = ClangCodeCompletionItemTester(QExplicitlySharedDataPointer(context)); QExplicitlySharedDataPointer f; for (const auto& item : tester.items) { if (item->argumentHintDepth() == 1) { f = item; break; } } QVERIFY(f.data()); const QString itemDisplay = tester.itemData(f).toString() + tester.itemData(f, KTextEditor:: CodeCompletionModel::Arguments).toString(); QCOMPARE(QStringLiteral("f(int i, int j, double k)"), itemDisplay); } void TestCodeCompletion::testCompleteFunction() { QFETCH(QString, code); QFETCH(CompletionItems, expectedItems); QFETCH(QString, itemToExecute); QFETCH(QString, expectedCode); auto executeItem = [=] (const ClangCodeCompletionItemTester& tester) { auto item = tester.findItem(itemToExecute); QVERIFY(item); auto view = createView(tester.completionContext->duContext()->url().toUrl(), this); item->execute(view.get(), view->document()->wordRangeAt(expectedItems.position)); QCOMPARE(view->document()->text(), expectedCode); }; executeCompletionTest(code, expectedItems, NoMacroOrBuiltin, executeItem); } void TestCodeCompletion::testCompleteFunction_data() { QTest::addColumn("code"); QTest::addColumn("expectedItems"); QTest::addColumn("itemToExecute"); QTest::addColumn("expectedCode"); QTest::newRow("add-parens") << "int foo();\nint main() {\n\n}" << CompletionItems({2, 0}, {"foo", "main"}) << "foo" << "int foo();\nint main() {\nfoo()\n}"; QTest::newRow("keep-parens") << "int foo();\nint main() {\nfoo();\n}" << CompletionItems({2, 0}, {"foo", "main"}) << "main" << "int foo();\nint main() {\nmain();\n}"; } diff --git a/languages/clang/tests/test_duchain.cpp b/languages/clang/tests/test_duchain.cpp index 5ed077f8fe..dfa7e72312 100644 --- a/languages/clang/tests/test_duchain.cpp +++ b/languages/clang/tests/test_duchain.cpp @@ -1,1368 +1,1513 @@ /* * Copyright 2014 Milian Wolff * Copyright 2014 Kevin Funk * Copyright 2015 Sergey Kalinichev * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "test_duchain.h" #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "duchain/clangparsingenvironmentfile.h" #include "duchain/clangparsingenvironment.h" #include "duchain/parsesession.h" #include #include QTEST_MAIN(TestDUChain); using namespace KDevelop; class TestEnvironmentProvider final : public IDefinesAndIncludesManager::BackgroundProvider { public: virtual ~TestEnvironmentProvider() = default; virtual QHash< QString, QString > definesInBackground(const QString& /*path*/) const override { return defines; } virtual Path::List includesInBackground(const QString& /*path*/) const override { return includes; } virtual IDefinesAndIncludesManager::Type type() const override { return IDefinesAndIncludesManager::UserDefined; } QHash defines; Path::List includes; }; TestDUChain::~TestDUChain() = default; void TestDUChain::initTestCase() { QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevelop.plugins.clang.debug=true\n")); QVERIFY(qputenv("KDEV_DISABLE_PLUGINS", "kdevcppsupport")); AutoTestShell::init({QStringLiteral("kdevclangsupport")}); - TestCore::initialize(); + auto core = TestCore::initialize(); + delete core->projectController(); + m_projectController = new TestProjectController(core); + core->setProjectController(m_projectController); } void TestDUChain::cleanupTestCase() { TestCore::shutdown(); } void TestDUChain::cleanup() { if (m_provider) { IDefinesAndIncludesManager::manager()->unregisterBackgroundProvider(m_provider.data()); } } void TestDUChain::init() { m_provider.reset(new TestEnvironmentProvider); IDefinesAndIncludesManager::manager()->registerBackgroundProvider(m_provider.data()); } struct ExpectedComment { QString identifier; QString comment; }; Q_DECLARE_METATYPE(ExpectedComment) Q_DECLARE_METATYPE(AbstractType::WhichType) void TestDUChain::testComments() { QFETCH(QString, code); QFETCH(ExpectedComment, expectedComment); TestFile file(code, "cpp"); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); auto candidates = top->findDeclarations(QualifiedIdentifier(expectedComment.identifier)); QVERIFY(!candidates.isEmpty()); auto decl = candidates.first(); QString comment = QString::fromLocal8Bit(decl->comment()); comment = KDevelop::htmlToPlainText(comment, KDevelop::CompleteMode); QCOMPARE(comment, expectedComment.comment); } void TestDUChain::testComments_data() { QTest::addColumn("code"); QTest::addColumn("expectedComment"); // note: Clang only retrieves the comments when in doxygen-style format (i.e. '///', '/**', '///<') QTest::newRow("invalid1") << "//this is foo\nint foo;" << ExpectedComment{"foo", QString()}; QTest::newRow("invalid2") << "/*this is foo*/\nint foo;" << ExpectedComment{"foo", QString()}; QTest::newRow("basic1") << "///this is foo\nint foo;" << ExpectedComment{"foo", "this is foo"}; QTest::newRow("basic2") << "/**this is foo*/\nint foo;" << ExpectedComment{"foo", "this is foo"}; QTest::newRow("enumerator") << "enum Foo { bar1, ///localDeclarations().size(), 2); auto decl = file.topContext()->localDeclarations()[1]; QVERIFY(decl); auto function = dynamic_cast(decl); QVERIFY(function); auto functionType = function->type(); QVERIFY(functionType); QEXPECT_FAIL("namespace", "The ElaboratedType is not exposed through the libclang interface, not much we can do here", Abort); QVERIFY(functionType->returnType()->whichType() != AbstractType::TypeDelayed); QEXPECT_FAIL("typedef", "After using clang_getCanonicalType on ElaboratedType all typedef information get's stripped away", Continue); QCOMPARE(functionType->returnType()->whichType(), type); } void TestDUChain::testElaboratedType_data() { QTest::addColumn("code"); QTest::addColumn("type"); QTest::newRow("namespace") << "namespace NS{struct Type{};} struct NS::Type foo();" << AbstractType::TypeStructure; QTest::newRow("enum") << "enum Enum{}; enum Enum foo();" << AbstractType::TypeEnumeration; QTest::newRow("typedef") << "namespace NS{typedef int type;} NS::type foo();" << AbstractType::TypeAlias; } void TestDUChain::testInclude() { TestFile header("int foo() { return 42; }\n", "h"); // NOTE: header is _not_ explictly being parsed, instead the impl job does that TestFile impl("#include \"" + header.url().byteArray() + "\"\n" "int main() { return foo(); }", "cpp", &header); impl.parse(TopDUContext::AllDeclarationsContextsAndUses); auto implCtx = impl.topContext(); QVERIFY(implCtx); DUChainReadLocker lock; QCOMPARE(implCtx->localDeclarations().size(), 1); auto headerCtx = DUChain::self()->chainForDocument(header.url()); QVERIFY(headerCtx); QVERIFY(!headerCtx->parsingEnvironmentFile()->needsUpdate()); QCOMPARE(headerCtx->localDeclarations().size(), 1); QVERIFY(implCtx->imports(headerCtx, CursorInRevision(0, 10))); Declaration* foo = headerCtx->localDeclarations().first(); QCOMPARE(foo->uses().size(), 1); QCOMPARE(foo->uses().begin().key(), impl.url()); QCOMPARE(foo->uses().begin()->size(), 1); QCOMPARE(foo->uses().begin()->first(), RangeInRevision(1, 20, 1, 23)); } QByteArray createCode(const QByteArray& prefix, const int functions) { QByteArray code; code += "#ifndef " + prefix + "_H\n"; code += "#define " + prefix + "_H\n"; for (int i = 0; i < functions; ++i) { code += "void myFunc_" + prefix + "(int arg1, char arg2, const char* arg3);\n"; } code += "#endif\n"; return code; } void TestDUChain::testIncludeLocking() { TestFile header1(createCode("Header1", 1000), "h"); TestFile header2(createCode("Header2", 1000), "h"); TestFile header3(createCode("Header3", 1000), "h"); ICore::self()->languageController()->backgroundParser()->setThreadCount(3); TestFile impl1("#include \"" + header1.url().byteArray() + "\"\n" "#include \"" + header2.url().byteArray() + "\"\n" "#include \"" + header3.url().byteArray() + "\"\n" "int main() { return 0; }", "cpp"); TestFile impl2("#include \"" + header2.url().byteArray() + "\"\n" "#include \"" + header1.url().byteArray() + "\"\n" "#include \"" + header3.url().byteArray() + "\"\n" "int main() { return 0; }", "cpp"); TestFile impl3("#include \"" + header3.url().byteArray() + "\"\n" "#include \"" + header1.url().byteArray() + "\"\n" "#include \"" + header2.url().byteArray() + "\"\n" "int main() { return 0; }", "cpp"); impl1.parse(TopDUContext::AllDeclarationsContextsAndUses); impl2.parse(TopDUContext::AllDeclarationsContextsAndUses); impl3.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(impl1.waitForParsed(5000)); QVERIFY(impl2.waitForParsed(5000)); QVERIFY(impl3.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(DUChain::self()->chainForDocument(header1.url())); QVERIFY(DUChain::self()->chainForDocument(header2.url())); QVERIFY(DUChain::self()->chainForDocument(header3.url())); } void TestDUChain::testReparse() { TestFile file("int main() { int i = 42; return i; }", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); DeclarationPointer mainDecl; DeclarationPointer iDecl; for (int i = 0; i < 3; ++i) { QVERIFY(file.waitForParsed(500)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 1); QCOMPARE(file.topContext()->localDeclarations().size(), 1); DUContext *exprContext = file.topContext()->childContexts().first()->childContexts().first(); QCOMPARE(exprContext->localDeclarations().size(), 1); if (i) { QVERIFY(mainDecl); QCOMPARE(mainDecl.data(), file.topContext()->localDeclarations().first()); QVERIFY(iDecl); QCOMPARE(iDecl.data(), exprContext->localDeclarations().first()); } mainDecl = file.topContext()->localDeclarations().first(); iDecl = exprContext->localDeclarations().first(); QVERIFY(mainDecl->uses().isEmpty()); QCOMPARE(iDecl->uses().size(), 1); QCOMPARE(iDecl->uses().begin()->size(), 1); if (i == 1) { file.setFileContents("int main()\n{\nfloat i = 13; return i - 5;\n}\n"); } file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); } } void TestDUChain::testReparseError() { TestFile file("int i = 1 / 0;\n", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); for (int i = 0; i < 2; ++i) { QVERIFY(file.waitForParsed(500)); DUChainReadLocker lock; QVERIFY(file.topContext()); if (!i) { QCOMPARE(file.topContext()->problems().size(), 1); file.setFileContents("int i = 0;\n"); } else { QCOMPARE(file.topContext()->problems().size(), 0); } file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); } } void TestDUChain::testTemplate() { TestFile file("template struct foo { T bar; };\n" "int main() { foo myFoo; return myFoo.bar; }\n", "cpp"); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto fooDecl = file.topContext()->localDeclarations().first(); QVERIFY(fooDecl->internalContext()); QCOMPARE(fooDecl->internalContext()->localDeclarations().size(), 2); QCOMPARE(file.topContext()->findDeclarations(QualifiedIdentifier("foo")).size(), 1); QCOMPARE(file.topContext()->findDeclarations(QualifiedIdentifier("foo::bar")).size(), 1); auto mainCtx = file.topContext()->localDeclarations().last()->internalContext()->childContexts().first(); QVERIFY(mainCtx); auto myFoo = mainCtx->localDeclarations().first(); QVERIFY(myFoo); QCOMPARE(myFoo->abstractType()->toString().remove(' '), QStringLiteral("foo")); } void TestDUChain::testNamespace() { TestFile file("namespace foo { struct bar { int baz; }; }\n" "int main() { foo::bar myBar; }\n", "cpp"); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto fooDecl = file.topContext()->localDeclarations().first(); QVERIFY(fooDecl->internalContext()); QCOMPARE(fooDecl->internalContext()->localDeclarations().size(), 1); DUContext* top = file.topContext().data(); DUContext* mainCtx = file.topContext()->childContexts().last(); auto foo = top->localDeclarations().first(); QCOMPARE(foo->qualifiedIdentifier().toString(), QString("foo")); DUContext* fooCtx = file.topContext()->childContexts().first(); QCOMPARE(fooCtx->localScopeIdentifier().toString(), QString("foo")); QCOMPARE(fooCtx->scopeIdentifier(true).toString(), QString("foo")); QCOMPARE(fooCtx->localDeclarations().size(), 1); auto bar = fooCtx->localDeclarations().first(); QCOMPARE(bar->qualifiedIdentifier().toString(), QString("foo::bar")); QCOMPARE(fooCtx->childContexts().size(), 1); DUContext* barCtx = fooCtx->childContexts().first(); QCOMPARE(barCtx->localScopeIdentifier().toString(), QString("bar")); QCOMPARE(barCtx->scopeIdentifier(true).toString(), QString("foo::bar")); QCOMPARE(barCtx->localDeclarations().size(), 1); auto baz = barCtx->localDeclarations().first(); QCOMPARE(baz->qualifiedIdentifier().toString(), QString("foo::bar::baz")); for (auto ctx : {top, mainCtx}) { QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo")).size(), 1); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo::bar")).size(), 1); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo::bar::baz")).size(), 1); } } void TestDUChain::testAutoTypeDeduction() { - TestFile file("const volatile auto foo = 5;\n", "cpp"); + TestFile file(R"( + const volatile auto foo = 5; + template struct myTemplate {}; + myTemplate& > templRefParam; + auto autoTemplRefParam = templRefParam; + )", "cpp"); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* ctx = file.topContext().data(); QVERIFY(ctx); - QCOMPARE(ctx->localDeclarations().size(), 1); + QCOMPARE(ctx->localDeclarations().size(), 4); QCOMPARE(ctx->findDeclarations(QualifiedIdentifier("foo")).size(), 1); Declaration* decl = ctx->findDeclarations(QualifiedIdentifier("foo"))[0]; QCOMPARE(decl->identifier(), Identifier("foo")); #if CINDEX_VERSION_MINOR < 31 QEXPECT_FAIL("", "No type deduction here unfortunately, missing API in Clang", Continue); #endif QVERIFY(decl->type()); #if CINDEX_VERSION_MINOR < 31 QCOMPARE(decl->toString(), QStringLiteral("const volatile auto foo")); #else QCOMPARE(decl->toString(), QStringLiteral("const volatile int foo")); #endif + + decl = ctx->findDeclarations(QualifiedIdentifier("autoTemplRefParam"))[0]; + QVERIFY(decl); + QVERIFY(decl->abstractType()); +#if CINDEX_VERSION_MINOR < 31 + QEXPECT_FAIL("", "Auto type is not exposed via LibClang", Continue); +#endif + QCOMPARE(decl->abstractType()->toString(), QStringLiteral("myTemplate< myTemplate< int >& >")); } void TestDUChain::testTypeDeductionInTemplateInstantiation() { // see: http://clang-developers.42468.n3.nabble.com/RFC-missing-libclang-query-functions-features-td2504253.html TestFile file("template struct foo { T member; } foo f; auto i = f.member;", "cpp"); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* ctx = file.topContext().data(); QVERIFY(ctx); QCOMPARE(ctx->localDeclarations().size(), 3); Declaration* decl = 0; // check 'foo' declaration decl = ctx->localDeclarations()[0]; QVERIFY(decl); QCOMPARE(decl->identifier(), Identifier("foo")); // check type of 'member' inside declaration-scope QCOMPARE(ctx->childContexts().size(), 1); DUContext* fooCtx = ctx->childContexts().first(); QVERIFY(fooCtx); // Should there really be two declarations? QCOMPARE(fooCtx->localDeclarations().size(), 2); decl = fooCtx->localDeclarations()[1]; QCOMPARE(decl->identifier(), Identifier("member")); // check type of 'member' in definition of 'f' decl = ctx->localDeclarations()[1]; QCOMPARE(decl->identifier(), Identifier("f")); decl = ctx->localDeclarations()[2]; QCOMPARE(decl->identifier(), Identifier("i")); #if CINDEX_VERSION_MINOR < 31 QEXPECT_FAIL("", "No type deduction here unfortunately, missing API in Clang", Continue); #endif QVERIFY(decl->type()); } void TestDUChain::testVirtualMemberFunction() { //Forward-declarations with "struct" or "class" are considered equal, so make sure the override is detected correctly. TestFile file("struct S {}; struct A { virtual S* ret(); }; struct B : public A { virtual S* ret(); };", "cpp"); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* top = file.topContext().data(); QVERIFY(top); QCOMPARE(top->childContexts().count(), 3); QCOMPARE(top->localDeclarations().count(), 3); QCOMPARE(top->childContexts()[2]->localDeclarations().count(), 1); Declaration* decl = top->childContexts()[2]->localDeclarations()[0]; QCOMPARE(decl->identifier(), Identifier("ret")); QVERIFY(DUChainUtils::getOverridden(decl)); } void TestDUChain::testBaseClasses() { TestFile file("class Base {}; class Inherited : public Base {};", "cpp"); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* top = file.topContext().data(); QVERIFY(top); QCOMPARE(top->localDeclarations().count(), 2); Declaration* baseDecl = top->localDeclarations().first(); QCOMPARE(baseDecl->identifier(), Identifier("Base")); ClassDeclaration* inheritedDecl = dynamic_cast(top->localDeclarations()[1]); QCOMPARE(inheritedDecl->identifier(), Identifier("Inherited")); QVERIFY(inheritedDecl); QCOMPARE(inheritedDecl->baseClassesSize(), 1u); QCOMPARE(baseDecl->uses().count(), 1); QCOMPARE(baseDecl->uses().first().count(), 1); QCOMPARE(baseDecl->uses().first().first(), RangeInRevision(0, 40, 0, 44)); } void TestDUChain::testReparseBaseClasses() { TestFile file("struct a{}; struct b : a {};\n", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); for (int i = 0; i < 2; ++i) { qDebug() << "run: " << i; QVERIFY(file.waitForParsed(500)); DUChainWriteLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 2); QCOMPARE(file.topContext()->childContexts().first()->importers().size(), 1); QCOMPARE(file.topContext()->childContexts().last()->importedParentContexts().size(), 1); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto aDecl = dynamic_cast(file.topContext()->localDeclarations().first()); QVERIFY(aDecl); QCOMPARE(aDecl->baseClassesSize(), 0u); auto bDecl = dynamic_cast(file.topContext()->localDeclarations().last()); QVERIFY(bDecl); QCOMPARE(bDecl->baseClassesSize(), 1u); int distance = 0; QVERIFY(bDecl->isPublicBaseClass(aDecl, file.topContext(), &distance)); QCOMPARE(distance, 1); file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); } } void TestDUChain::testReparseBaseClassesTemplates() { TestFile file("template struct a{}; struct b : a {};\n", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); for (int i = 0; i < 2; ++i) { qDebug() << "run: " << i; QVERIFY(file.waitForParsed(500)); DUChainWriteLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 2); QCOMPARE(file.topContext()->childContexts().first()->importers().size(), 1); QCOMPARE(file.topContext()->childContexts().last()->importedParentContexts().size(), 1); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto aDecl = dynamic_cast(file.topContext()->localDeclarations().first()); QVERIFY(aDecl); QCOMPARE(aDecl->baseClassesSize(), 0u); auto bDecl = dynamic_cast(file.topContext()->localDeclarations().last()); QVERIFY(bDecl); QCOMPARE(bDecl->baseClassesSize(), 1u); int distance = 0; QVERIFY(bDecl->isPublicBaseClass(aDecl, file.topContext(), &distance)); QCOMPARE(distance, 1); file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); } } +// TODO: Move this test to kdevplatform. +void TestDUChain::testGetInheriters() +{ + TestFile file("class Base { class Inner {}; }; class Inherited : public Base, Base::Inner {};", "cpp"); + QVERIFY(file.parseAndWait()); + + DUChainReadLocker lock; + DUContext* top = file.topContext().data(); + QVERIFY(top); + + QCOMPARE(top->localDeclarations().count(), 2); + Declaration* baseDecl = top->localDeclarations().first(); + QCOMPARE(baseDecl->identifier(), Identifier("Base")); + + DUContext* baseCtx = baseDecl->internalContext(); + QVERIFY(baseCtx); + QCOMPARE(baseCtx->localDeclarations().count(), 1); + + Declaration* innerDecl = baseCtx->localDeclarations().first(); + QCOMPARE(innerDecl->identifier(), Identifier("Inner")); + + Declaration* inheritedDecl = top->localDeclarations()[1]; + QVERIFY(inheritedDecl); + QCOMPARE(inheritedDecl->identifier(), Identifier("Inherited")); + + uint maxAllowedSteps = uint(-1); + auto baseInheriters = DUChainUtils::getInheriters(baseDecl, maxAllowedSteps); + QEXPECT_FAIL("", "not yet working properly, tentative fix is up for review for kdevplatform", Abort); + QCOMPARE(baseInheriters, QList() << inheritedDecl); + + maxAllowedSteps = uint(-1); + auto innerInheriters = DUChainUtils::getInheriters(innerDecl, maxAllowedSteps); + QCOMPARE(innerInheriters, QList() << inheritedDecl); + + maxAllowedSteps = uint(-1); + auto inheritedInheriters = DUChainUtils::getInheriters(inheritedDecl, maxAllowedSteps); + QCOMPARE(inheritedInheriters.count(), 0); +} + void TestDUChain::testGlobalFunctionDeclaration() { TestFile file("void foo(int arg1, char arg2);\n", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); file.waitForParsed(); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 1); QCOMPARE(file.topContext()->childContexts().size(), 1); QVERIFY(!file.topContext()->childContexts().first()->inSymbolTable()); } void TestDUChain::testFunctionDefinitionVsDeclaration() { TestFile file("void func(); void func() {}\n", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed()); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto funcDecl = file.topContext()->localDeclarations()[0]; QVERIFY(!funcDecl->isDefinition()); QVERIFY(!dynamic_cast(funcDecl)); auto funcDef = file.topContext()->localDeclarations()[1]; QVERIFY(dynamic_cast(funcDef)); QVERIFY(funcDef->isDefinition()); } void TestDUChain::testEnsureNoDoubleVisit() { // On some language construct, we may up visiting the same cursor multiple times // Example: "struct SomeStruct {} s;" // decl: "SomeStruct SomeStruct " of kind StructDecl (2) in main.cpp@[(1,1),(1,17)] // decl: "struct SomeStruct s " of kind VarDecl (9) in main.cpp@[(1,1),(1,19)] // decl: "SomeStruct SomeStruct " of kind StructDecl (2) in main.cpp@[(1,1),(1,17)] // // => We end up visiting the StructDecl twice (or more) // That's because we use clang_visitChildren not just on the translation unit cursor. // Apparently just "recursing" vs. "visiting children explicitly" // results in a different AST traversal TestFile file("struct SomeStruct {} s;\n", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed()); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); // there should only be one declaration for "SomeStruct" auto candidates = top->findDeclarations(QualifiedIdentifier("SomeStruct")); QCOMPARE(candidates.size(), 1); } void TestDUChain::testParsingEnvironment() { const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses; IndexedTopDUContext indexed; ClangParsingEnvironment lastEnv; { TestFile file("int main() {}\n", "cpp"); auto astFeatures = static_cast(features | TopDUContext::AST); file.parse(astFeatures); file.setKeepDUChainData(true); QVERIFY(file.waitForParsed()); DUChainWriteLocker lock; auto top = file.topContext(); QVERIFY(top); auto sessionData = ParseSessionData::Ptr(dynamic_cast(top->ast().data())); lock.unlock(); ParseSession session(sessionData); lock.lock(); QVERIFY(session.data()); QVERIFY(top); auto envFile = QExplicitlySharedDataPointer( dynamic_cast(file.topContext()->parsingEnvironmentFile().data())); QCOMPARE(envFile->features(), astFeatures); QVERIFY(envFile->featuresSatisfied(astFeatures)); QCOMPARE(envFile->environmentQuality(), ClangParsingEnvironment::Source); // if no environment is given, no update should be triggered QVERIFY(!envFile->needsUpdate()); // same env should also not trigger a reparse ClangParsingEnvironment env = session.environment(); QCOMPARE(env.quality(), ClangParsingEnvironment::Source); QVERIFY(!envFile->needsUpdate(&env)); // but changing the environment should trigger an update env.addIncludes(Path::List() << Path("/foo/bar/baz")); QVERIFY(envFile->needsUpdate(&env)); envFile->setEnvironment(env); QVERIFY(!envFile->needsUpdate(&env)); // setting the environment quality higher should require an update env.setQuality(ClangParsingEnvironment::BuildSystem); QVERIFY(envFile->needsUpdate(&env)); envFile->setEnvironment(env); QVERIFY(!envFile->needsUpdate(&env)); // changing defines requires an update env.addDefines(QHash{ { "foo", "bar" } }); QVERIFY(envFile->needsUpdate(&env)); // but only when changing the defines for the envFile's TU const auto barTU = IndexedString("bar.cpp"); const auto oldTU = env.translationUnitUrl(); env.setTranslationUnitUrl(barTU); QCOMPARE(env.translationUnitUrl(), barTU); QVERIFY(!envFile->needsUpdate(&env)); env.setTranslationUnitUrl(oldTU); QVERIFY(envFile->needsUpdate(&env)); // update it again envFile->setEnvironment(env); QVERIFY(!envFile->needsUpdate(&env)); lastEnv = env; // now compare against a lower quality environment // in such a case, we do not want to trigger an update env.setQuality(ClangParsingEnvironment::Unknown); env.setTranslationUnitUrl(barTU); QVERIFY(!envFile->needsUpdate(&env)); // even when the environment changes env.addIncludes(Path::List() << Path("/lalalala")); QVERIFY(!envFile->needsUpdate(&env)); indexed = top->indexed(); } DUChain::self()->storeToDisk(); { DUChainWriteLocker lock; QVERIFY(!DUChain::self()->isInMemory(indexed.index())); QVERIFY(indexed.data()); QVERIFY(DUChain::self()->environmentFileForDocument(indexed)); auto envFile = QExplicitlySharedDataPointer( dynamic_cast(DUChain::self()->environmentFileForDocument(indexed).data())); QVERIFY(envFile); QCOMPARE(envFile->features(), features); QVERIFY(envFile->featuresSatisfied(features)); QVERIFY(!envFile->needsUpdate(&lastEnv)); DUChain::self()->removeDocumentChain(indexed.data()); } } void TestDUChain::testActiveDocumentHasASTAttached() { const TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses; IndexedTopDUContext indexed; ClangParsingEnvironment lastEnv; { TestFile file("int main() {}\n", "cpp"); auto astFeatures = static_cast(features | TopDUContext::AST); file.parse(astFeatures); file.setKeepDUChainData(true); QVERIFY(file.waitForParsed()); DUChainWriteLocker lock; auto top = file.topContext(); QVERIFY(top); auto sessionData = ParseSessionData::Ptr(dynamic_cast(top->ast().data())); lock.unlock(); ParseSession session(sessionData); lock.lock(); QVERIFY(session.data()); QVERIFY(top); QVERIFY(top->ast()); indexed = top->indexed(); } DUChain::self()->storeToDisk(); { DUChainWriteLocker lock; QVERIFY(!DUChain::self()->isInMemory(indexed.index())); QVERIFY(indexed.data()); } QUrl url; { DUChainReadLocker lock; auto ctx = indexed.data(); QVERIFY(ctx); QVERIFY(!ctx->ast()); url = ctx->url().toUrl(); } // Here the file is already deleted, so clang_parseTranslationUnit2 will fail, but we still get ParseSessionData attached. auto document = ICore::self()->documentController()->openDocument(url); QVERIFY(document); ICore::self()->documentController()->activateDocument(document); QApplication::processEvents(); ICore::self()->languageController()->backgroundParser()->parseDocuments(); QThread::sleep(1); document->close(KDevelop::IDocument::Discard); { DUChainReadLocker lock; auto ctx = indexed.data(); QVERIFY(ctx); QVERIFY(ctx->ast()); } DUChainWriteLocker lock; DUChain::self()->removeDocumentChain(indexed.data()); } void TestDUChain::testSystemIncludes() { ClangParsingEnvironment env; Path::List projectIncludes = { Path("/projects/1"), Path("/projects/1/sub"), Path("/projects/2"), Path("/projects/2/sub") }; env.addIncludes(projectIncludes); auto includes = env.includes(); // no project paths set, so everything is considered a system include QCOMPARE(includes.system, projectIncludes); QVERIFY(includes.project.isEmpty()); Path::List systemIncludes = { Path("/sys"), Path("/sys/sub") }; env.addIncludes(systemIncludes); includes = env.includes(); QCOMPARE(includes.system, projectIncludes + systemIncludes); QVERIFY(includes.project.isEmpty()); Path::List projects = { Path("/projects/1"), Path("/projects/2") }; env.setProjectPaths(projects); // now the list should be properly separated QCOMPARE(env.projectPaths(), projects); includes = env.includes(); QCOMPARE(includes.system, systemIncludes); QCOMPARE(includes.project, projectIncludes); } void TestDUChain::benchDUChainBuilder() { QBENCHMARK_ONCE { TestFile file( "#include \n" "#include \n" "#include \n" "#include \n" "#include \n" "#include \n" "#include \n" "#include \n" "#include \n" "#include \n", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(60000)); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); } } void TestDUChain::testReparseWithAllDeclarationsContextsAndUses() { TestFile file("int foo() { return 0; } int main() { return foo(); }", "cpp"); file.parse(TopDUContext::VisibleDeclarationsAndContexts); QVERIFY(file.waitForParsed(1000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 2); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto dec = file.topContext()->localDeclarations().at(0); QEXPECT_FAIL("", "Skipping of function bodies is disabled for now", Continue); QVERIFY(dec->uses().isEmpty()); } file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(500)); { DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->childContexts().size(), 2); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto mainDecl = file.topContext()->localDeclarations()[1]; QVERIFY(mainDecl->uses().isEmpty()); auto foo = file.topContext()->localDeclarations().first(); QCOMPARE(foo->uses().size(), 1); } } void TestDUChain::testReparseOnDocumentActivated() { TestFile file("int foo() { return 0; } int main() { return foo(); }", "cpp"); file.parse(TopDUContext::VisibleDeclarationsAndContexts); QVERIFY(file.waitForParsed(1000)); { DUChainReadLocker lock; auto ctx = file.topContext(); QVERIFY(ctx); QCOMPARE(ctx->childContexts().size(), 2); QCOMPARE(ctx->localDeclarations().size(), 2); auto dec = ctx->localDeclarations().at(0); QEXPECT_FAIL("", "Skipping of function bodies was disabled for now", Continue); QVERIFY(dec->uses().isEmpty()); QVERIFY(!ctx->ast()); } auto backgroundParser = ICore::self()->languageController()->backgroundParser(); QVERIFY(!backgroundParser->isQueued(file.url())); auto doc = ICore::self()->documentController()->openDocument(file.url().toUrl()); QVERIFY(doc); QVERIFY(backgroundParser->isQueued(file.url())); QSignalSpy spy(backgroundParser, &BackgroundParser::parseJobFinished); spy.wait(); doc->close(KDevelop::IDocument::Discard); { DUChainReadLocker lock; auto ctx = file.topContext(); QCOMPARE(ctx->features() & TopDUContext::AllDeclarationsContextsAndUses, static_cast(TopDUContext::AllDeclarationsContextsAndUses)); QVERIFY(ctx->topContext()->ast()); } } void TestDUChain::testReparseInclude() { TestFile header("int foo() { return 42; }\n", "h"); TestFile impl("#include \"" + header.url().byteArray() + "\"\n" "int main() { return foo(); }", "cpp", &header); // Use TopDUContext::AST to imitate that document is opened in the editor, so that ClangParseJob can store translation unit, that'll be used for reparsing. impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsAndContexts|TopDUContext::AST)); QVERIFY(impl.waitForParsed(5000)); { DUChainReadLocker lock; auto implCtx = impl.topContext(); QVERIFY(implCtx); QCOMPARE(implCtx->importedParentContexts().size(), 1); } impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST)); QVERIFY(impl.waitForParsed(5000)); DUChainReadLocker lock; auto implCtx = impl.topContext(); QVERIFY(implCtx); QCOMPARE(implCtx->localDeclarations().size(), 1); QCOMPARE(implCtx->importedParentContexts().size(), 1); auto headerCtx = DUChain::self()->chainForDocument(header.url()); QVERIFY(headerCtx); QVERIFY(!headerCtx->parsingEnvironmentFile()->needsUpdate()); QCOMPARE(headerCtx->localDeclarations().size(), 1); QVERIFY(implCtx->imports(headerCtx, CursorInRevision(0, 10))); Declaration* foo = headerCtx->localDeclarations().first(); QCOMPARE(foo->uses().size(), 1); QCOMPARE(foo->uses().begin().key(), impl.url()); QCOMPARE(foo->uses().begin()->size(), 1); QCOMPARE(foo->uses().begin()->first(), RangeInRevision(1, 20, 1, 23)); QCOMPARE(DUChain::self()->allEnvironmentFiles(header.url()).size(), 1); QCOMPARE(DUChain::self()->allEnvironmentFiles(impl.url()).size(), 1); QCOMPARE(DUChain::self()->chainsForDocument(header.url()).size(), 1); QCOMPARE(DUChain::self()->chainsForDocument(impl.url()).size(), 1); } void TestDUChain::testReparseChangeEnvironment() { TestFile header("int foo() { return 42; }\n", "h"); TestFile impl("#include \"" + header.url().byteArray() + "\"\n" "int main() { return foo(); }", "cpp", &header); uint hashes[3] = {0, 0, 0}; for (int i = 0; i < 3; ++i) { impl.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(impl.waitForParsed(5000)); { DUChainReadLocker lock; QVERIFY(impl.topContext()); auto env = dynamic_cast(impl.topContext()->parsingEnvironmentFile().data()); QVERIFY(env); QCOMPARE(env->environmentQuality(), ClangParsingEnvironment::Source); hashes[i] = env->environmentHash(); QVERIFY(hashes[i]); // we should never end up with multiple env files or chains in memory for these files QCOMPARE(DUChain::self()->allEnvironmentFiles(impl.url()).size(), 1); QCOMPARE(DUChain::self()->chainsForDocument(impl.url()).size(), 1); QCOMPARE(DUChain::self()->allEnvironmentFiles(header.url()).size(), 1); QCOMPARE(DUChain::self()->chainsForDocument(header.url()).size(), 1); } // in every run, we expect the environment to have changed for (int j = 0; j < i; ++j) { QVERIFY(hashes[i] != hashes[j]); } if (i == 0) { // 1) change defines m_provider->defines.insert("foooooooo", "baaar!"); } else if (i == 1) { // 2) change includes m_provider->includes.append(Path("/foo/bar/asdf/lalala")); } // 3) stop } } void TestDUChain::testMacrosRanges() { TestFile file("#define FUNC_MACROS(x) struct str##x{};\nFUNC_MACROS(x);", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto macroDefinition = file.topContext()->localDeclarations()[0]; QVERIFY(macroDefinition); QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,19)); auto structDeclaration = file.topContext()->localDeclarations()[1]; QVERIFY(structDeclaration); QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0)); QCOMPARE(macroDefinition->uses().size(), 1); QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,11)); } void TestDUChain::testMultiLineMacroRanges() { TestFile file("#define FUNC_MACROS(x) struct str##x{};\nFUNC_MACROS(x\n);", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 2); auto macroDefinition = file.topContext()->localDeclarations()[0]; QVERIFY(macroDefinition); QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,19)); auto structDeclaration = file.topContext()->localDeclarations()[1]; QVERIFY(structDeclaration); QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0)); QCOMPARE(macroDefinition->uses().size(), 1); QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,11)); } void TestDUChain::testNestedMacroRanges() { TestFile file("#define INNER int var; var = 0;\n#define MACRO() INNER\nint main(){MACRO(\n);}", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 3); auto main = file.topContext()->localDeclarations()[2]; QVERIFY(main); auto mainCtx = main->internalContext()->childContexts().first(); QVERIFY(mainCtx); QCOMPARE(mainCtx->localDeclarations().size(), 1); auto var = mainCtx->localDeclarations().first(); QVERIFY(var); QCOMPARE(var->range(), RangeInRevision(2,11,2,11)); QCOMPARE(var->uses().size(), 1); QCOMPARE(var->uses().begin()->first(), RangeInRevision(2,11,2,11)); } void TestDUChain::testNestedImports() { TestFile B("#pragma once\nint B();\n", "h"); TestFile C("#pragma once\n#include \"" + B.url().byteArray() + "\"\nint C();\n", "h"); TestFile A("#include \"" + B.url().byteArray() + "\"\n" + "#include \"" + C.url().byteArray() + "\"\nint A();\n", "cpp"); A.parse(); QVERIFY(A.waitForParsed(5000)); DUChainReadLocker lock; auto BCtx = DUChain::self()->chainForDocument(B.url().toUrl()); QVERIFY(BCtx); QVERIFY(BCtx->importedParentContexts().isEmpty()); auto CCtx = DUChain::self()->chainForDocument(C.url().toUrl()); QVERIFY(CCtx); QCOMPARE(CCtx->importedParentContexts().size(), 1); QVERIFY(CCtx->imports(BCtx, CursorInRevision(1, 10))); auto ACtx = A.topContext(); QVERIFY(ACtx); QCOMPARE(ACtx->importedParentContexts().size(), 2); QVERIFY(ACtx->imports(BCtx, CursorInRevision(0, 10))); QVERIFY(ACtx->imports(CCtx, CursorInRevision(1, 10))); } void TestDUChain::testEnvironmentWithDifferentOrderOfElements() { TestFile file("int main();\n", "cpp"); m_provider->includes.clear(); m_provider->includes.append(Path("/path1")); m_provider->includes.append(Path("/path2")); m_provider->defines.clear(); m_provider->defines.insert("key1", "value1"); m_provider->defines.insert("key2", "value2"); m_provider->defines.insert("key3", "value3"); uint previousHash = 0; for (int i: {0, 1, 2, 3}) { file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(file.waitForParsed(5000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); auto env = dynamic_cast(file.topContext()->parsingEnvironmentFile().data()); QVERIFY(env); QCOMPARE(env->environmentQuality(), ClangParsingEnvironment::Source); if (previousHash) { if (i == 3) { QVERIFY(previousHash != env->environmentHash()); } else { QCOMPARE(previousHash, env->environmentHash()); } } previousHash = env->environmentHash(); QVERIFY(previousHash); } if (i == 0) { //Change order of defines. Hash of the environment should stay the same. m_provider->defines.clear(); m_provider->defines.insert("key3", "value3"); m_provider->defines.insert("key1", "value1"); m_provider->defines.insert("key2", "value2"); } else if (i == 1) { //Add the same macros twice. Hash of the environment should stay the same. m_provider->defines.clear(); m_provider->defines.insert("key2", "value2"); m_provider->defines.insert("key3", "value3"); m_provider->defines.insert("key3", "value3"); m_provider->defines.insert("key1", "value1"); } else if (i == 2) { //OTOH order of includes should change hash of the environment. m_provider->includes.clear(); m_provider->includes.append(Path("/path2")); m_provider->includes.append(Path("/path1")); } } } void TestDUChain::testReparseMacro() { TestFile file("#define DECLARE(a) typedef struct a##_ {} *a;\nDECLARE(D);\nD d;", "cpp"); file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST)); QVERIFY(file.waitForParsed(5000)); { DUChainReadLocker lock; QVERIFY(file.topContext()); } file.parse(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses|TopDUContext::AST|TopDUContext::ForceUpdate)); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 5); auto macroDefinition = file.topContext()->localDeclarations()[0]; QVERIFY(macroDefinition); QCOMPARE(macroDefinition->range(), RangeInRevision(0,8,0,15)); QCOMPARE(macroDefinition->uses().size(), 1); QCOMPARE(macroDefinition->uses().begin()->first(), RangeInRevision(1,0,1,7)); auto structDeclaration = file.topContext()->localDeclarations()[1]; QVERIFY(structDeclaration); QCOMPARE(structDeclaration->range(), RangeInRevision(1,0,1,0)); auto structTypedef = file.topContext()->localDeclarations()[3]; QVERIFY(structTypedef); QCOMPARE(structTypedef->range(), RangeInRevision(1,8,1,9)); QCOMPARE(structTypedef->uses().size(), 1); QCOMPARE(structTypedef->uses().begin()->first(), RangeInRevision(2,0,2,1)); } void TestDUChain::testGotoStatement() { TestFile file("int main() {\ngoto label;\ngoto label;\nlabel: return 0;}", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 1); auto main = file.topContext()->localDeclarations()[0]; QVERIFY(main); auto mainCtx = main->internalContext()->childContexts().first(); QVERIFY(mainCtx); QCOMPARE(mainCtx->localDeclarations().size(), 1); auto label = mainCtx->localDeclarations().first(); QVERIFY(label); QCOMPARE(label->range(), RangeInRevision(3,0,3,5)); QCOMPARE(label->uses().size(), 1); QCOMPARE(label->uses().begin()->first(), RangeInRevision(1,5,1,10)); QCOMPARE(label->uses().begin()->last(), RangeInRevision(2,5,2,10)); } void TestDUChain::testRangesOfOperatorsInsideMacro() { TestFile file("class Test{public: Test& operator++(int);};\n#define MACRO(var) var++;\nint main(){\nTest tst; MACRO(tst)}", "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed(5000)); DUChainReadLocker lock; QVERIFY(file.topContext()); QCOMPARE(file.topContext()->localDeclarations().size(), 3); auto testClass = file.topContext()->localDeclarations()[0]; QVERIFY(testClass); auto operatorPlusPlus = testClass->internalContext()->localDeclarations().first(); QVERIFY(operatorPlusPlus); QCOMPARE(operatorPlusPlus->uses().size(), 1); QCOMPARE(operatorPlusPlus->uses().begin()->first(), RangeInRevision(3,10,3,10)); } void TestDUChain::testUsesCreatedForDeclarations() { auto code = R"(template void functionTemplate(T); template void functionTemplate(U) {} namespace NS { class Class{}; } using NS::Class; Class function(); NS::Class function() { return {}; } int main () { functionTemplate(int()); function(); } )"; TestFile file(code, "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed()); DUChainReadLocker lock; QVERIFY(file.topContext()); auto functionTemplate = file.topContext()->findDeclarations(QualifiedIdentifier("functionTemplate")); QVERIFY(!functionTemplate.isEmpty()); auto functionTemplateDeclaration = DUChainUtils::declarationForDefinition(functionTemplate.first()); QVERIFY(!functionTemplateDeclaration->isDefinition()); #if CINDEX_VERSION_MINOR < 29 QEXPECT_FAIL("", "No API in LibClang to determine function template type", Continue); #endif QCOMPARE(functionTemplateDeclaration->uses().count(), 1); auto function = file.topContext()->findDeclarations(QualifiedIdentifier("function")); QVERIFY(!function.isEmpty()); auto functionDeclaration = DUChainUtils::declarationForDefinition(function.first()); QVERIFY(!functionDeclaration->isDefinition()); QCOMPARE(functionDeclaration->uses().count(), 1); } void TestDUChain::testReparseIncludeGuard() { TestFile header("#ifndef GUARD\n#define GUARD\nint something;\n#endif\n", "h"); TestFile impl("#include \"" + header.url().byteArray() + "\"\n", "cpp", &header); impl.parseAndWait(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST )); { DUChainReadLocker lock; QCOMPARE(static_cast(impl.topContext()-> importedParentContexts().first().context(impl.topContext()))->problems().size(), 0); } impl.parseAndWait(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); { DUChainReadLocker lock; QCOMPARE(static_cast(impl.topContext()-> importedParentContexts().first().context(impl.topContext()))->problems().size(), 0); } } void TestDUChain::testExternC() { auto code = R"(extern "C" { void foo(); })"; TestFile file(code, "cpp"); file.parse(TopDUContext::AllDeclarationsContextsAndUses); QVERIFY(file.waitForParsed()); DUChainReadLocker lock; auto top = file.topContext(); QVERIFY(top); QVERIFY(!top->findDeclarations(QualifiedIdentifier("foo")).isEmpty()); } void TestDUChain::testReparseUnchanged_data() { QTest::addColumn("headerCode"); QTest::addColumn("implCode"); QTest::newRow("include-guards") << R"( #ifndef GUARD #define GUARD int something; #endif )" << R"( #include "%1" )"; QTest::newRow("template-default-parameters") << R"( #ifndef TEST_H #define TEST_H template class dummy; template class dummy { int field[T]; }; #endif )" << R"( #include "%1" int main(int, char **) { dummy<> x; (void)x; } )"; } void TestDUChain::testReparseUnchanged() { QFETCH(QString, headerCode); QFETCH(QString, implCode); TestFile header(headerCode, "h"); TestFile impl(implCode.arg(header.url().str()), "cpp", &header); auto checkProblems = [&] (bool reparsed) { DUChainReadLocker lock; auto headerCtx = DUChain::self()->chainForDocument(header.url()); QVERIFY(headerCtx); QVERIFY(headerCtx->problems().isEmpty()); auto implCtx = DUChain::self()->chainForDocument(impl.url()); QVERIFY(implCtx); if (reparsed && CINDEX_VERSION_MINOR > 29) { QEXPECT_FAIL("template-default-parameters", "the precompiled preamble messes the default template parameters up in clang 3.7", Continue); } QVERIFY(implCtx->problems().isEmpty()); }; impl.parseAndWait(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST )); checkProblems(false); impl.parseAndWait(TopDUContext::Features(TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::ForceUpdateRecursive)); checkProblems(true); } + +void TestDUChain::testTypeAliasTemplate() +{ + TestFile file("template using TypeAliasTemplate = T;", "cpp"); + QVERIFY(file.parseAndWait()); + + DUChainReadLocker lock; + QVERIFY(file.topContext()); + + auto templateAlias = file.topContext()->localDeclarations().last(); + QVERIFY(templateAlias); +#if CINDEX_VERSION_MINOR < 31 + QEXPECT_FAIL("", "TypeAliasTemplate is not exposed via LibClang", Abort); +#endif + QVERIFY(templateAlias->abstractType()); + QCOMPARE(templateAlias->abstractType()->toString(), QStringLiteral("TypeAliasTemplate")); +} + +static bool containsErrors(const QList& problems) +{ + auto it = std::find_if(problems.begin(), problems.end(), [] (const Problem::Ptr& problem) { + return problem->severity() == Problem::Error; + }); + return it != problems.end(); +} + +static bool expectedXmmintrinErrors(const QList& problems) +{ + foreach (const auto& problem, problems) { + if (problem->severity() == Problem::Error && !problem->description().contains("Cannot initialize a parameter of type")) { + return false; + } + } + return true; +} + +static void verifyNoErrors(TopDUContext* top, QSet& checked) +{ + const auto problems = top->problems(); + if (containsErrors(problems)) { + qDebug() << top->url() << top->problems(); + if (top->url().str().endsWith("xmmintrin.h") && expectedXmmintrinErrors(problems)) { + QEXPECT_FAIL("", "there are still some errors in xmmintrin.h b/c some clang provided intrinsincs are more strict than the GCC ones.", Continue); + QVERIFY(false); + } else { + QFAIL("parse error detected"); + } + } + const auto imports = top->importedParentContexts(); + foreach (const auto& import, imports) { + auto ctx = import.context(top); + QVERIFY(ctx); + auto importedTop = ctx->topContext(); + if (checked.contains(importedTop)) { + continue; + } + checked.insert(importedTop); + verifyNoErrors(importedTop, checked); + } +} + +void TestDUChain::testGccCompatibility() +{ + // TODO: make it easier to change the compiler provider for testing purposes + QTemporaryDir dir; + auto project = new TestProject(Path(dir.path()), this); + auto definesAndIncludesConfig = project->projectConfiguration()->group("CustomDefinesAndIncludes"); + auto pathConfig = definesAndIncludesConfig.group("ProjectPath0"); + pathConfig.writeEntry("Path", "."); + pathConfig.group("Compiler").writeEntry("Name", "GCC"); + m_projectController->addProject(project); + + { + TestFile file(R"( + #include + + int main() { return 0; } + )", "c", project, dir.path()); + + file.parse(); + QVERIFY(file.waitForParsed(5000)); + + DUChainReadLocker lock; + QSet checked; + verifyNoErrors(file.topContext(), checked); + } + + m_projectController->clearProjects(); +} diff --git a/languages/clang/tests/test_duchain.h b/languages/clang/tests/test_duchain.h index 4873152962..5ffbac4f86 100644 --- a/languages/clang/tests/test_duchain.h +++ b/languages/clang/tests/test_duchain.h @@ -1,88 +1,96 @@ /* * Copyright 2014 Milian Wolff * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef DUCHAINTEST_H #define DUCHAINTEST_H #include class TestEnvironmentProvider; +namespace KDevelop { +class TestProjectController; +} + class TestDUChain : public QObject { Q_OBJECT public: ~TestDUChain(); private slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); void testComments(); void testComments_data(); void testElaboratedType(); void testElaboratedType_data(); void testInclude(); void testIncludeLocking(); void testReparse(); void testReparseError(); void testTemplate(); void testNamespace(); void testAutoTypeDeduction(); void testTypeDeductionInTemplateInstantiation(); void testVirtualMemberFunction(); void testBaseClasses(); void testReparseBaseClasses(); void testReparseBaseClassesTemplates(); + void testGetInheriters(); void testGlobalFunctionDeclaration(); void testFunctionDefinitionVsDeclaration(); void testEnsureNoDoubleVisit(); void testReparseWithAllDeclarationsContextsAndUses(); void testReparseOnDocumentActivated(); void testParsingEnvironment(); void testSystemIncludes(); void testReparseInclude(); void testReparseChangeEnvironment(); void testMacrosRanges(); void testNestedImports(); void testEnvironmentWithDifferentOrderOfElements(); void testReparseMacro(); void testMultiLineMacroRanges(); void testNestedMacroRanges(); void testGotoStatement(); void testRangesOfOperatorsInsideMacro(); void testActiveDocumentHasASTAttached(); void testUsesCreatedForDeclarations(); void testReparseIncludeGuard(); void testExternC(); void testReparseUnchanged_data(); void testReparseUnchanged(); + void testTypeAliasTemplate(); void benchDUChainBuilder(); + void testGccCompatibility(); private: QScopedPointer m_provider; + KDevelop::TestProjectController* m_projectController; }; #endif // DUCHAINTEST_H diff --git a/languages/clang/util/clangutils.cpp b/languages/clang/util/clangutils.cpp index 5c5c0491b4..e3594c92e3 100644 --- a/languages/clang/util/clangutils.cpp +++ b/languages/clang/util/clangutils.cpp @@ -1,335 +1,338 @@ /* * Copyright 2014 Kevin Funk * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "clangutils.h" #include "../util/clangdebug.h" #include "../util/clangtypes.h" #include "../duchain/cursorkindtraits.h" #include #include #include #include using namespace KDevelop; CXCursor ClangUtils::getCXCursor(int line, int column, const CXTranslationUnit& unit, const CXFile& file) { if (!file) { clangDebug() << "getCXCursor couldn't find file: " << clang_getFileName(file); return clang_getNullCursor(); } CXSourceLocation location = clang_getLocation(unit, file, line + 1, column + 1); if (clang_equalLocations(clang_getNullLocation(), location)) { clangDebug() << "getCXCursor given invalid position " << line << ", " << column << " for file " << clang_getFileName(file); return clang_getNullCursor(); } return clang_getCursor(unit, location); } namespace { struct FunctionInfo { KTextEditor::Range range; QString fileName; CXTranslationUnit unit; QStringList stringParts; }; CXChildVisitResult paramVisitor(CXCursor cursor, CXCursor /*parent*/, CXClientData data) { //Ignore the type of the parameter CXCursorKind kind = clang_getCursorKind(cursor); if (kind == CXCursor_TypeRef || kind == CXCursor_TemplateRef || kind == CXCursor_NamespaceRef) { return CXChildVisit_Continue; } FunctionInfo *info = static_cast(data); ClangRange range(clang_getCursorExtent(cursor)); CXFile file; clang_getFileLocation(clang_getCursorLocation(cursor),&file,nullptr,nullptr,nullptr); if (!file) { clangDebug() << "Couldn't find file associated with default parameter cursor!"; //We keep going, because getting an error because we accidentally duplicated //a default parameter is better than deleting a default parameter } QString fileName = ClangString(clang_getFileName(file)).toString(); //Clang doesn't make a distinction between the default arguments being in //the declaration or definition, and the default arguments don't have lexical //parents. So this range check is the only thing that really works. if ((info->fileName.isEmpty() || fileName == info->fileName) && info->range.contains(range.toRange())) { const ClangTokens tokens(info->unit, range.range()); for (CXToken token : tokens) { info->stringParts.append(ClangString(clang_getTokenSpelling(info->unit, token)).toString()); } } return CXChildVisit_Continue; } } QVector ClangUtils::getDefaultArguments(CXCursor cursor, DefaultArgumentsMode mode) { if (!CursorKindTraits::isFunction(clang_getCursorKind(cursor))) { return QVector(); } int numArgs = clang_Cursor_getNumArguments(cursor); QVector arguments(mode == FixedSize ? numArgs : 0); QString fileName; CXFile file; clang_getFileLocation(clang_getCursorLocation(cursor),&file,nullptr,nullptr,nullptr); if (!file) { clangDebug() << "Couldn't find file associated with default parameter cursor!"; //The empty string serves as a wildcard string, because it's better to //duplicate a default parameter than delete one } else { fileName = ClangString(clang_getFileName(file)).toString(); } FunctionInfo info{ClangRange(clang_getCursorExtent(cursor)).toRange(), fileName, clang_Cursor_getTranslationUnit(cursor), QStringList()}; for (int i = 0; i < numArgs; i++) { CXCursor arg = clang_Cursor_getArgument(cursor, i); info.stringParts.clear(); clang_visitChildren(arg, paramVisitor, &info); //Clang includes the equal sign sometimes, but not other times. if (!info.stringParts.isEmpty() && info.stringParts.first() == QLatin1String("=")) { info.stringParts.removeFirst(); } //Clang seems to include the , or ) at the end of the param, so delete that if (!info.stringParts.isEmpty() && (info.stringParts.last() == QLatin1String(",") || info.stringParts.last() == QLatin1String(")"))) { info.stringParts.removeLast(); } const QString result = info.stringParts.join(QString()); if (mode == FixedSize) { arguments.replace(i, result); } else if (!result.isEmpty()) { arguments << result; } } return arguments; } bool ClangUtils::isFileEqual(CXFile file1, CXFile file2) { #if CINDEX_VERSION_MINOR >= 28 return clang_File_isEqual(file1, file2); #else // note: according to the implementation of clang_File_isEqual, file1 and file2 can still be equal, // regardless of whether file1 == file2 is true or not // however, we didn't see any problems with pure pointer comparisions until now, so fall back to that return file1 == file2; #endif } -constexpr bool isScopeKind(CXCursorKind kind) +bool ClangUtils::isScopeKind(CXCursorKind kind) { return kind == CXCursor_Namespace || kind == CXCursor_StructDecl || kind == CXCursor_UnionDecl || kind == CXCursor_ClassDecl || kind == CXCursor_ClassTemplate || kind == CXCursor_ClassTemplatePartialSpecialization; } -QString ClangUtils::getScope(CXCursor cursor) +QString ClangUtils::getScope(CXCursor cursor, CXCursor context) { QStringList scope; - CXCursor destContext = clang_getCanonicalCursor(clang_getCursorLexicalParent(cursor)); + if (clang_Cursor_isNull(context)) { + context = clang_getCursorLexicalParent(cursor); + } + context = clang_getCanonicalCursor(context); CXCursor search = clang_getCursorSemanticParent(cursor); - while (isScopeKind(clang_getCursorKind(search)) && !clang_equalCursors(search, destContext)) { - scope.prepend(ClangString(clang_getCursorSpelling(search)).toString()); + while (isScopeKind(clang_getCursorKind(search)) && !clang_equalCursors(search, context)) { + scope.prepend(ClangString(clang_getCursorDisplayName(search)).toString()); search = clang_getCursorSemanticParent(search); } return scope.join(QStringLiteral("::")); } QString ClangUtils::getCursorSignature(CXCursor cursor, const QString& scope, const QVector& defaultArgs) { CXCursorKind kind = clang_getCursorKind(cursor); //Get the return type QString ret; ret.reserve(128); QTextStream stream(&ret); if (kind != CXCursor_Constructor && kind != CXCursor_Destructor) { stream << ClangString(clang_getTypeSpelling(clang_getCursorResultType(cursor))).toString() << ' '; } //Build the function name, with scope and parameters if (!scope.isEmpty()) { stream << scope << "::"; } QString functionName = ClangString(clang_getCursorSpelling(cursor)).toString(); if (functionName.contains(QLatin1Char('<'))) { stream << functionName.left(functionName.indexOf(QLatin1Char('<'))); } else { stream << functionName; } //Add the parameters and such stream << '('; int numArgs = clang_Cursor_getNumArguments(cursor); for (int i = 0; i < numArgs; i++) { CXCursor arg = clang_Cursor_getArgument(cursor, i); //Clang formats pointer types as "t *x" and reference types as "t &x", while //KDevelop formats them as "t* x" and "t& x". Make that adjustment. const QString type = ClangString(clang_getTypeSpelling(clang_getCursorType(arg))).toString(); if (type.endsWith(QLatin1String(" *")) || type.endsWith(QLatin1String(" &"))) { stream << type.left(type.length() - 2) << type.at(type.length() - 1); } else { stream << type; } const QString id = ClangString(clang_getCursorDisplayName(arg)).toString(); if (!id.isEmpty()) { stream << ' ' << id; } if (i < defaultArgs.count() && !defaultArgs.at(i).isEmpty()) { stream << " = " << defaultArgs.at(i); } if (i < numArgs - 1) { stream << ", "; } } if (clang_Cursor_isVariadic(cursor)) { if (numArgs > 0) { stream << ", "; } stream << "..."; } stream << ')'; if (clang_CXXMethod_isConst(cursor)) { stream << " const"; } return ret; } QByteArray ClangUtils::getRawContents(CXTranslationUnit unit, CXSourceRange range) { const auto rangeStart = clang_getRangeStart(range); const auto rangeEnd = clang_getRangeEnd(range); unsigned int start, end; clang_getFileLocation(rangeStart, nullptr, nullptr, nullptr, &start); clang_getFileLocation(rangeEnd, nullptr, nullptr, nullptr, &end); QByteArray result; const ClangTokens tokens(unit, range); for (CXToken token : tokens) { const auto location = ClangLocation(clang_getTokenLocation(unit, token)); unsigned int offset; clang_getFileLocation(location, nullptr, nullptr, nullptr, &offset); Q_ASSERT(offset >= start); const int fillCharacters = offset - start - result.size(); Q_ASSERT(fillCharacters >= 0); result.append(QByteArray(fillCharacters, ' ')); const auto spelling = clang_getTokenSpelling(unit, token); result.append(clang_getCString(spelling)); clang_disposeString(spelling); } // Clang always appends the full range of the last token, even if this exceeds the end of the requested range. // Fix this. result.chop((result.size() - 1) - (end - start)); return result; } bool ClangUtils::isExplicitlyDefaultedOrDeleted(CXCursor cursor) { if (clang_getCursorAvailability(cursor) == CXAvailability_NotAvailable) { return true; } // TODO: expose clang::FunctionDecl::isExplicitlyDefaulted() in libclang // For symmetry we should probably also expose clang::FunctionDecl::isDeleted() auto declCursor = clang_getCanonicalCursor(cursor); CXTranslationUnit tu = clang_Cursor_getTranslationUnit(declCursor); ClangTokens tokens(tu, clang_getCursorExtent(declCursor)); bool lastTokenWasDeleteOrDefault = false; for (auto it = tokens.rbegin(), end = tokens.rend(); it != end; ++it) { CXToken token = *it; auto kind = clang_getTokenKind(token); switch (kind) { case CXToken_Comment: break; case CXToken_Identifier: case CXToken_Literal: lastTokenWasDeleteOrDefault = false; break; case CXToken_Punctuation: { ClangString spelling(clang_getTokenSpelling(tu, token)); const char* spellingCStr = spelling.c_str(); if (strcmp(spellingCStr, ")") == 0) { // a closing parent means we have reached the end of the function parameter list // therefore this function can't be explicitly deleted/defaulted return false; } else if (strcmp(spellingCStr, "=") == 0) { if (lastTokenWasDeleteOrDefault) { return true; } #if CINDEX_VERSION_MINOR < 31 // HACK: on old clang versions, we don't get the default/delete // so there, assume the function is defaulted or deleted // when the last token is an equal sign if (it == tokens.rbegin()) { return true; } #endif } lastTokenWasDeleteOrDefault = false; break; } case CXToken_Keyword: { ClangString spelling(clang_getTokenSpelling(tu, token)); const char* spellingCStr = spelling.c_str(); if (strcmp(spellingCStr, "default") == 0 #if CINDEX_VERSION_MINOR < 31 || strcmp(spellingCStr, "delete") == 0 #endif ) { lastTokenWasDeleteOrDefault = true; } else { lastTokenWasDeleteOrDefault = false; } break; } } } return false; } diff --git a/languages/clang/util/clangutils.h b/languages/clang/util/clangutils.h index 807939cfd3..07627c0664 100644 --- a/languages/clang/util/clangutils.h +++ b/languages/clang/util/clangutils.h @@ -1,119 +1,126 @@ /* * Copyright 2014 Kevin Funk * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef CLANGUTILS_H #define CLANGUTILS_H #include #include namespace KDevelop { class IndexedString; } namespace ClangUtils { /** * Finds the most specific CXCursor which applies to the the specified line and column * in the given translation unit and file. * * @param line The 0-indexed line number at which to search. * @param column The 0-indexed column number at which to search. * @param unit The translation unit to examine. * @param file The file in the translation unit to examine. * * @return The cursor at the specified location */ CXCursor getCXCursor(int line, int column, const CXTranslationUnit& unit, const CXFile& file); enum DefaultArgumentsMode { FixedSize, ///< The vector will have length equal to the number of arguments to the function /// and any arguments without a default parameter will be represented with an empty string. MinimumSize ///< The vector will have a length equal to the number of default values }; /** * Given a cursor representing a function, returns a vector containing the string * representations of the default arguments of the function which are defined at * the occurance of the cursor. Note that this is not necessarily all of the default * arguments of the function. * * @param cursor The cursor to examine * @return a vector of QStrings representing the default arguments, or an empty * vector if cursor does not represent a function */ QVector getDefaultArguments(CXCursor cursor, DefaultArgumentsMode mode = FixedSize); + /** + * @return true when the cursor kind references a named scope. + */ + bool isScopeKind(CXCursorKind kind); + /** * Given a cursor and destination context, returns the string representing the * cursor's scope at its current location. * * @param cursor The cursor to examine + * @param context The destination context from which the cursor should be referenced. + * By default this will be set to the cursors lexical parent. * @return the cursor's scope as a string */ - QString getScope(CXCursor cursor); + QString getScope(CXCursor cursor, CXCursor context = clang_getNullCursor()); /** * Given a cursor representing some sort of function, returns its signature. The * effect of this function when passed a non-function cursor is undefined. * * @param cursor The cursor to work with * @param scope The scope of the cursor (e.g. "SomeNS::SomeClass") * @return A QString of the function's signature */ QString getCursorSignature(CXCursor cursor, const QString& scope, const QVector& defaultArgs = QVector()); /** * Extract the raw contents of the range @p range * * @note This will return the exact textual representation of the code, * no whitespace stripped, etc. * * TODO: It would better if we'd be able to just memcpy parts of the file buffer * that's stored inside Clang (cf. llvm::MemoryBuffer for files), but libclang * doesn't offer API for that. This implementation here is a lot more expensive. * * @param unit Translation unit this range is part of */ QByteArray getRawContents(CXTranslationUnit unit, CXSourceRange range); /** * @brief Return true if file @p file1 and file @p file2 are equal * * @see clang_File_isEqual */ bool isFileEqual(CXFile file1, CXFile file2); /** * @brief Return true if the cursor @p cursor refers to an explicitly deleted/defaulted function * such as the default constructor in "struct Foo { Foo() = delete; }" * * TODO: do we need isExplicitlyDefaulted() + isExplicitlyDeleted()? * Currently this is only used by the implements completion to hide deleted+defaulted functions so * we don't need to know the difference. We need to tokenize the source code because there is no * such API in libclang so having one function to check both cases is more efficient (only tokenize once) */ bool isExplicitlyDefaultedOrDeleted(CXCursor cursor); }; #endif // CLANGUTILS_H diff --git a/languages/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp b/languages/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp index f2d58f4fb9..a49ce05a27 100644 --- a/languages/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp +++ b/languages/plugins/custom-definesandincludes/compilerprovider/settingsmanager.cpp @@ -1,354 +1,361 @@ /* * This file is part of KDevelop * * Copyright 2010 Andreas Pakulat * Copyright 2014 Sergey Kalinichev * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "settingsmanager.h" #include #include #include #include #include #include #include #include #include #include #include "compilerprovider.h" using namespace KDevelop; namespace { namespace ConfigConstants { const QString configKey = QLatin1String( "CustomDefinesAndIncludes" ); const QString definesKey = QLatin1String( "Defines" ); const QString includesKey = QLatin1String( "Includes" ); const QString projectPathPrefix = QLatin1String( "ProjectPath" ); const QString projectPathKey = QLatin1String( "Path" ); const QString customBuildSystemGroup = QLatin1String( "CustomBuildSystem" ); const QString definesAndIncludesGroup = QLatin1String( "Defines And Includes" ); const QString compilersGroup = QLatin1String( "Compilers" ); const QString compilerNameKey = QLatin1String( "Name" ); const QString compilerPathKey = QLatin1String( "Path" ); const QString compilerTypeKey = QLatin1String( "Type" ); QString parserArguments() { return QStringLiteral("parserArguments"); } } // the grouplist is randomly sorted b/c it uses QSet internally // we sort the keys here, as the structure is properly defined for us QStringList sorted(QStringList list) { std::sort(list.begin(), list.end()); return list; } QString defaultArguments() { return QStringLiteral("-ferror-limit=100 -fspell-checking -Wdocumentation -Wunused-parameter -Wunreachable-code -Wall -std=c++11"); } CompilerPointer createCompilerFromConfig(KConfigGroup& cfg) { auto grp = cfg.group("Compiler"); auto name = grp.readEntry( ConfigConstants::compilerNameKey, QString() ); if (name.isEmpty()) { return SettingsManager::globalInstance()->provider()->checkCompilerExists({}); } for (auto c : SettingsManager::globalInstance()->provider()->compilers()) { if (c->name() == name) { return c; } } // Otherwise we have no such compiler registered (broken config file), return default one return SettingsManager::globalInstance()->provider()->checkCompilerExists({}); } void writeCompilerToConfig(KConfigGroup& cfg, const CompilerPointer& compiler) { Q_ASSERT(compiler); auto grp = cfg.group("Compiler"); // Store only compiler name, path and type retrieved from registered compilers grp.writeEntry(ConfigConstants::compilerNameKey, compiler->name()); } void doWriteSettings( KConfigGroup grp, const QList& paths ) { int pathIndex = 0; for ( const auto& path : paths ) { KConfigGroup pathgrp = grp.group( ConfigConstants::projectPathPrefix + QString::number( pathIndex++ ) ); pathgrp.writeEntry(ConfigConstants::projectPathKey, path.path); pathgrp.writeEntry(ConfigConstants::parserArguments(), path.parserArguments); { int index = 0; KConfigGroup includes(pathgrp.group(ConfigConstants::includesKey)); for( auto it = path.includes.begin() ; it != path.includes.end(); ++it){ includes.writeEntry(QString::number(++index), *it); } } { KConfigGroup defines(pathgrp.group(ConfigConstants::definesKey)); for (auto it = path.defines.begin(); it != path.defines.end(); ++it) { defines.writeEntry(it.key(), it.value()); } } writeCompilerToConfig(pathgrp, path.compiler); } } /// @param remove if true all read entries will be removed from the config file QList doReadSettings( KConfigGroup grp, bool remove = false ) { QList paths; for( const QString &grpName : sorted(grp.groupList()) ) { if ( !grpName.startsWith( ConfigConstants::projectPathPrefix ) ) { continue; } KConfigGroup pathgrp = grp.group( grpName ); ConfigEntry path; path.path = pathgrp.readEntry( ConfigConstants::projectPathKey, "" ); path.parserArguments = pathgrp.readEntry(ConfigConstants::parserArguments(), defaultArguments()); + if (path.parserArguments.isEmpty()) { + path.parserArguments = defaultArguments(); + } + { // defines // Backwards compatibility with old config style if(pathgrp.hasKey(ConfigConstants::definesKey)) { QByteArray tmp = pathgrp.readEntry( ConfigConstants::definesKey, QByteArray() ); QDataStream s( tmp ); s.setVersion( QDataStream::Qt_4_5 ); // backwards compatible reading QHash defines; s >> defines; path.setDefines(defines); } else { KConfigGroup defines(pathgrp.group(ConfigConstants::definesKey)); QMap defMap = defines.entryMap(); path.defines.reserve(defMap.size()); for(auto it = defMap.constBegin(); it != defMap.constEnd(); ++it) { QString key = it.key(); if(key.isEmpty()) { // Toss out the invalid key and value since a valid // value needs a valid key continue; } else { path.defines.insert(key, it.value()); } } } } { // includes // Backwards compatibility with old config style if(pathgrp.hasKey(ConfigConstants::includesKey)){ QByteArray tmp = pathgrp.readEntry( ConfigConstants::includesKey, QByteArray() ); QDataStream s( tmp ); s.setVersion( QDataStream::Qt_4_5 ); s >> path.includes; } else { KConfigGroup includes(pathgrp.group(ConfigConstants::includesKey)); QMap incMap = includes.entryMap(); for(auto it = incMap.begin(); it != incMap.end(); ++it){ QString value = it.value(); if(value.isEmpty()){ continue; } path.includes += value; } } } path.compiler = createCompilerFromConfig(pathgrp); if ( remove ) { pathgrp.deleteGroup(); } + + Q_ASSERT(!path.parserArguments.isEmpty()); paths << path; } return paths; } /** * Reads and converts paths from old (Custom Build System's) format to the current one. * @return all converted paths (if any) */ QList convertedPaths( KConfig* cfg ) { KConfigGroup group = cfg->group( ConfigConstants::customBuildSystemGroup ); if ( !group.isValid() ) return {}; QList paths; foreach( const QString &grpName, sorted(group.groupList()) ) { KConfigGroup subgroup = group.group( grpName ); if ( !subgroup.isValid() ) continue; paths += doReadSettings( subgroup, true ); } return paths; } } void ConfigEntry::setDefines(const QHash& newDefines) { defines.clear(); defines.reserve(newDefines.size()); for (auto it = newDefines.begin(); it != newDefines.end(); ++it) { defines[it.key()] = it.value().toString(); } } SettingsManager::SettingsManager() : m_provider(this) {} SettingsManager::~SettingsManager() {} SettingsManager* SettingsManager::globalInstance() { Q_ASSERT(QThread::currentThread() == qApp->thread()); static SettingsManager s_globalInstance; return &s_globalInstance; } CompilerProvider* SettingsManager::provider() { return &m_provider; } const CompilerProvider* SettingsManager::provider() const { return &m_provider; } void SettingsManager::writePaths( KConfig* cfg, const QList< ConfigEntry >& paths ) { Q_ASSERT(QThread::currentThread() == qApp->thread()); KConfigGroup grp = cfg->group( ConfigConstants::configKey ); if ( !grp.isValid() ) return; grp.deleteGroup(); doWriteSettings( grp, paths ); } QList SettingsManager::readPaths( KConfig* cfg ) const { Q_ASSERT(QThread::currentThread() == qApp->thread()); auto converted = convertedPaths( cfg ); if ( !converted.isEmpty() ) { const_cast(this)->writePaths( cfg, converted ); return converted; } KConfigGroup grp = cfg->group( ConfigConstants::configKey ); if ( !grp.isValid() ) { return {}; } return doReadSettings( grp ); } bool SettingsManager::needToReparseCurrentProject( KConfig* cfg ) const { auto grp = cfg->group( ConfigConstants::definesAndIncludesGroup ); return grp.readEntry( "reparse", true ); } void SettingsManager::writeUserDefinedCompilers(const QVector< CompilerPointer >& compilers) { QVector< CompilerPointer > editableCompilers; for (const auto& compiler : compilers) { if (!compiler->editable()) { continue; } editableCompilers.append(compiler); } KConfigGroup config = KSharedConfig::openConfig()->group(ConfigConstants::compilersGroup); config.deleteGroup(); config.writeEntry("number", editableCompilers.count()); int i = 0; for (const auto& compiler : editableCompilers) { KConfigGroup grp = config.group(QString::number(i)); ++i; grp.writeEntry(ConfigConstants::compilerNameKey, compiler->name()); grp.writeEntry(ConfigConstants::compilerPathKey, compiler->path()); grp.writeEntry(ConfigConstants::compilerTypeKey, compiler->factoryName()); } config.sync(); } QVector< CompilerPointer > SettingsManager::userDefinedCompilers() const { QVector< CompilerPointer > compilers; KConfigGroup config = KSharedConfig::openConfig()->group(ConfigConstants::compilersGroup); int count = config.readEntry("number", 0); for (int i = 0; i < count; i++) { KConfigGroup grp = config.group(QString::number(i)); auto name = grp.readEntry(ConfigConstants::compilerNameKey, QString()); auto path = grp.readEntry(ConfigConstants::compilerPathKey, QString()); auto type = grp.readEntry(ConfigConstants::compilerTypeKey, QString()); auto cf = m_provider.compilerFactories(); for (auto f : cf) { if (f->name() == type) { auto compiler = f->createCompiler(name, path); compilers.append(compiler); } } } return compilers; } QString SettingsManager::defaultParserArguments() const { return defaultArguments(); } ConfigEntry::ConfigEntry(const QString& path) : path(path) , compiler(SettingsManager::globalInstance()->provider()->checkCompilerExists({})) + , parserArguments(defaultArguments()) {} diff --git a/languages/plugins/custom-definesandincludes/definesandincludesmanager.cpp b/languages/plugins/custom-definesandincludes/definesandincludesmanager.cpp index 858dac19c2..8a912cc995 100644 --- a/languages/plugins/custom-definesandincludes/definesandincludesmanager.cpp +++ b/languages/plugins/custom-definesandincludes/definesandincludesmanager.cpp @@ -1,294 +1,292 @@ /* * This file is part of KDevelop * * Copyright 2014 Sergey Kalinichev * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "definesandincludesmanager.h" #include "kcm_widget/definesandincludesconfigpage.h" #include "compilerprovider/compilerprovider.h" #include "noprojectincludesanddefines/noprojectincludepathsmanager.h" #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { ///@return: The ConfigEntry, with includes/defines from @p paths for all parent folders of @p item. static ConfigEntry findConfigForItem(QList paths, const KDevelop::ProjectBaseItem* item) { ConfigEntry ret; const Path itemPath = item->path(); const Path rootDirectory = item->project()->path(); Path closestPath; std::sort(paths.begin(), paths.end(), [] (const ConfigEntry& lhs, const ConfigEntry& rhs) { // sort in reverse order to do a bottom-up search return lhs.path > rhs.path; }); for (const ConfigEntry & entry : paths) { Path targetDirectory = rootDirectory; // note: a dot represents the project root if (entry.path != ".") { targetDirectory.addPath(entry.path); } if (targetDirectory == itemPath || targetDirectory.isParentOf(itemPath)) { ret.includes += entry.includes; for (auto it = entry.defines.constBegin(); it != entry.defines.constEnd(); it++) { if (!ret.defines.contains(it.key())) { ret.defines[it.key()] = it.value(); } } if (ret.parserArguments.isEmpty() || targetDirectory.segments().size() > closestPath.segments().size()) { ret.parserArguments = entry.parserArguments; closestPath = targetDirectory; } } } ret.includes.removeDuplicates(); - if (ret.parserArguments.isEmpty()) { - ret.parserArguments = SettingsManager::globalInstance()->defaultParserArguments(); - } + Q_ASSERT(!ret.parserArguments.isEmpty()); return ret; } void merge(Defines* target, const Defines& source) { if (target->isEmpty()) { *target = source; return; } for (auto it = source.constBegin(); it != source.constEnd(); ++it) { target->insert(it.key(), it.value()); } } } K_PLUGIN_FACTORY_WITH_JSON(DefinesAndIncludesManagerFactory, "kdevdefinesandincludesmanager.json", registerPlugin(); ) DefinesAndIncludesManager::DefinesAndIncludesManager( QObject* parent, const QVariantList& ) : IPlugin("kdevdefinesandincludesmanager", parent ) , m_settings(SettingsManager::globalInstance()) , m_noProjectIPM(new NoProjectIncludePathsManager()) { KDEV_USE_EXTENSION_INTERFACE(IDefinesAndIncludesManager); registerProvider(m_settings->provider()); } DefinesAndIncludesManager::~DefinesAndIncludesManager() = default; Defines DefinesAndIncludesManager::defines( ProjectBaseItem* item, Type type ) const { Q_ASSERT(QThread::currentThread() == qApp->thread()); if (!item) { return m_settings->provider()->defines(nullptr); } Defines defines; for (auto provider : m_providers) { if (provider->type() & type) { merge(&defines, provider->defines(item)); } } if ( type & ProjectSpecific ) { auto buildManager = item->project()->buildSystemManager(); if ( buildManager ) { merge(&defines, buildManager->defines(item)); } } // Manually set defines have the highest priority and overwrite values of all other types of defines. if (type & UserDefined) { auto cfg = item->project()->projectConfiguration().data(); merge(&defines, findConfigForItem(m_settings->readPaths(cfg), item).defines); } return defines; } Path::List DefinesAndIncludesManager::includes( ProjectBaseItem* item, Type type ) const { Q_ASSERT(QThread::currentThread() == qApp->thread()); if (!item) { return m_settings->provider()->includes(nullptr); } Path::List includes; if (type & UserDefined) { auto cfg = item->project()->projectConfiguration().data(); includes += KDevelop::toPathList(findConfigForItem(m_settings->readPaths(cfg), item).includes); } if ( type & ProjectSpecific ) { auto buildManager = item->project()->buildSystemManager(); if ( buildManager ) { includes += buildManager->includeDirectories(item); } } for (auto provider : m_providers) { if (provider->type() & type) { includes += provider->includes(item); } } return includes; } bool DefinesAndIncludesManager::unregisterProvider(IDefinesAndIncludesManager::Provider* provider) { int idx = m_providers.indexOf(provider); if (idx != -1) { m_providers.remove(idx); return true; } return false; } void DefinesAndIncludesManager::registerProvider(IDefinesAndIncludesManager::Provider* provider) { Q_ASSERT(provider); if (m_providers.contains(provider)) { return; } m_providers.push_back(provider); } Defines DefinesAndIncludesManager::defines(const QString&) const { return m_settings->provider()->defines(nullptr); } Path::List DefinesAndIncludesManager::includes(const QString& path) const { return m_settings->provider()->includes(nullptr) + m_noProjectIPM->includes(path); } void DefinesAndIncludesManager::openConfigurationDialog(const QString& pathToFile) { if (auto project = KDevelop::ICore::self()->projectController()->findProjectForUrl(QUrl::fromLocalFile(pathToFile))) { KDevelop::ICore::self()->projectController()->configureProject(project); } else { m_noProjectIPM->openConfigurationDialog(pathToFile); } } Path::List DefinesAndIncludesManager::includesInBackground(const QString& path) const { Path::List includes; for (auto provider: m_backgroundProviders) { includes += provider->includesInBackground(path); } return includes; } Defines DefinesAndIncludesManager::definesInBackground(const QString& path) const { QHash defines; for (auto provider: m_backgroundProviders) { auto result = provider->definesInBackground(path); for (auto it = result.constBegin(); it != result.constEnd(); it++) { defines[it.key()] = it.value(); } } return defines; } bool DefinesAndIncludesManager::unregisterBackgroundProvider(IDefinesAndIncludesManager::BackgroundProvider* provider) { int idx = m_backgroundProviders.indexOf(provider); if (idx != -1) { m_backgroundProviders.remove(idx); return true; } return false; } void DefinesAndIncludesManager::registerBackgroundProvider(IDefinesAndIncludesManager::BackgroundProvider* provider) { Q_ASSERT(provider); if (m_backgroundProviders.contains(provider)) { return; } m_backgroundProviders.push_back(provider); } QString DefinesAndIncludesManager::parserArguments(KDevelop::ProjectBaseItem* item) const { if(!item){ return m_settings->defaultParserArguments(); } Q_ASSERT(QThread::currentThread() == qApp->thread()); auto cfg = item->project()->projectConfiguration().data(); return findConfigForItem(m_settings->readPaths(cfg), item).parserArguments; } int DefinesAndIncludesManager::perProjectConfigPages() const { return 1; } ConfigPage* DefinesAndIncludesManager::perProjectConfigPage(int number, const ProjectConfigOptions& options, QWidget* parent) { if (number == 0) { return new DefinesAndIncludesConfigPage(this, options, parent); } return nullptr; } #include "definesandincludesmanager.moc" diff --git a/languages/plugins/custom-definesandincludes/kcm_widget/projectpathsmodel.cpp b/languages/plugins/custom-definesandincludes/kcm_widget/projectpathsmodel.cpp index f14c6df0b3..c3a8418226 100644 --- a/languages/plugins/custom-definesandincludes/kcm_widget/projectpathsmodel.cpp +++ b/languages/plugins/custom-definesandincludes/kcm_widget/projectpathsmodel.cpp @@ -1,235 +1,237 @@ /************************************************************************ * * * Copyright 2010 Andreas Pakulat * * * * 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 or version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, see . * ************************************************************************/ #include "projectpathsmodel.h" #include #include #include #include using namespace KDevelop; ProjectPathsModel::ProjectPathsModel( QObject* parent ) : QAbstractListModel( parent ), project( 0 ) { } void ProjectPathsModel::setProject(IProject* w_project) { project = w_project; } QVariant ProjectPathsModel::data( const QModelIndex& index, int role ) const { if( !index.isValid() || index.row() < 0 || index.row() >= rowCount() || index.column() != 0 ) { return QVariant(); } const ConfigEntry& pathConfig = projectPaths.at( index.row() ); switch( role ) { case IncludesDataRole: return pathConfig.includes; break; case DefinesDataRole: return QVariant::fromValue(pathConfig.defines); break; case Qt::EditRole: return sanitizePath( pathConfig.path, true, false ); break; case Qt::DisplayRole: { const QString& path = pathConfig.path; return (path == ".") ? "(project root)" : path; break; } case FullUrlDataRole: return QVariant::fromValue(QUrl::fromUserInput( sanitizePath( pathConfig.path, true, false ) )); break; case CompilerDataRole: return QVariant::fromValue(pathConfig.compiler); break; case ParserArgumentsRole: return pathConfig.parserArguments; break; default: break; } return QVariant(); } int ProjectPathsModel::rowCount( const QModelIndex& parent ) const { if( parent.isValid() ) { return 0; } return projectPaths.count(); } bool ProjectPathsModel::setData( const QModelIndex& index, const QVariant& value, int role ) { if( !index.isValid() || index.row() < 0 || index.row() >= rowCount() || index.column() != 0 ) { return false; } // Do not allow to change path of the first entry; instead, add a new one in that case if( index.row() == 0 && ( role == Qt::EditRole || role == Qt::DisplayRole || role == FullUrlDataRole ) ) { QString addedPath = sanitizePath( value.toString(), false ); // Do not allow duplicates foreach( const ConfigEntry& existingConfig, projectPaths ) { if( addedPath == existingConfig.path ) { return false; } } projectPaths.insert( 1, sanitizePath( value.toString(), false ) ); emit dataChanged( this->index( 1, 0 ), this->index( projectPaths.count() - 1, 0 ) ); return true; } ConfigEntry& pathConfig = projectPaths[ index.row() ]; switch( role ) { case IncludesDataRole: pathConfig.includes = value.toStringList(); break; case DefinesDataRole: pathConfig.defines = value.value(); break; case Qt::EditRole: pathConfig.path = sanitizePath( value.toString(), false ); break; case Qt::DisplayRole: pathConfig.path = sanitizePath( value.toString(), true ); break; case FullUrlDataRole: pathConfig.path = sanitizeUrl( value.value() ); break; case CompilerDataRole: pathConfig.compiler = value.value(); break; case ParserArgumentsRole: pathConfig.parserArguments = value.toString(); break; default: return false; break; } emit dataChanged( index, index ); return true; } Qt::ItemFlags ProjectPathsModel::flags( const QModelIndex& index ) const { if( !index.isValid() ) { return 0; } if( index.row() == 0 ) { return Qt::ItemFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled ); } return Qt::ItemFlags( Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled ); } QList< ConfigEntry > ProjectPathsModel::paths() const { return projectPaths; } void ProjectPathsModel::setPaths(const QList< ConfigEntry >& paths ) { beginResetModel(); projectPaths.clear(); foreach( const ConfigEntry& existingPathConfig, paths ) { // Sanitize the path of loaded config ConfigEntry config = existingPathConfig; config.path = sanitizePath( config.path == "." ? QString() : config.path ); addPathInternal( config, false ); } addPathInternal( sanitizePath( QString() ), true ); // add an empty "root" config entry if one does not exist endResetModel(); } bool ProjectPathsModel::removeRows( int row, int count, const QModelIndex& parent ) { if( row >= 0 && count > 0 && row < rowCount() ) { beginRemoveRows( parent, row, row + count - 1 ); for( int i = 0; i < count; ++i ) { if( projectPaths.at(row).path == "." ) { continue; // we won't remove the root item } projectPaths.removeAt(row); } endRemoveRows(); return true; } return false; } void ProjectPathsModel::addPath( const QUrl &url ) { if( !project->path().isParentOf(KDevelop::Path(url)) ) { return; } beginInsertRows( QModelIndex(), rowCount(), rowCount() ); addPathInternal( sanitizeUrl(url), false ); endInsertRows(); } void ProjectPathsModel::addPathInternal( const ConfigEntry& config, bool prepend ) { + Q_ASSERT(!config.parserArguments.isEmpty()); + // Do not allow duplicates foreach( const ConfigEntry& existingConfig, projectPaths ) { if( config.path == existingConfig.path ) { return; } } if( prepend ) { projectPaths.prepend( config ); } else { projectPaths.append( config ); } } QString ProjectPathsModel::sanitizeUrl( QUrl url, bool needRelative ) const { Q_ASSERT( project ); if (needRelative) { const auto relativePath = project->path().relativePath(KDevelop::Path(url)); return relativePath.isEmpty() ? QStringLiteral(".") : relativePath; } return url.adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments).toString(QUrl::PreferLocalFile); } QString ProjectPathsModel::sanitizePath( const QString& path, bool expectRelative, bool needRelative ) const { Q_ASSERT( project ); Q_ASSERT( expectRelative || project->inProject(IndexedString(path)) ); QUrl url; if( expectRelative ) { url = KDevelop::Path(project->path(), path).toUrl(); } else { url = QUrl::fromUserInput(path); } return sanitizeUrl( url, needRelative ); } diff --git a/languages/qmljs/kdevqmljsplugin.cpp b/languages/qmljs/kdevqmljsplugin.cpp index 6ab8afca76..17b9879f67 100644 --- a/languages/qmljs/kdevqmljsplugin.cpp +++ b/languages/qmljs/kdevqmljsplugin.cpp @@ -1,183 +1,201 @@ /************************************************************************************* * Copyright (C) 2012 by Aleix Pol * * Copyright (C) 2012 by Milian Wolff * * Copyright (C) 2013 by Sven Brauch * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "kdevqmljsplugin.h" #include "qmljsparsejob.h" #include "qmljshighlighting.h" #include "codecompletion/model.h" #include "navigation/propertypreviewwidget.h" #include "duchain/helper.h" +#include + #include #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(KDevQmlJsSupportFactory, "kdevqmljs.json", registerPlugin(); ) using namespace KDevelop; +/// TODO: Extend? See qmljsmodelmanager.h in qt-creator.git +class ModelManager: public QmlJS::ModelManagerInterface +{ + Q_OBJECT + +public: + explicit ModelManager(QObject *parent = nullptr); + ~ModelManager(); +}; + +ModelManager::ModelManager(QObject* parent) + : QmlJS::ModelManagerInterface(parent) {} + +ModelManager::~ModelManager() {} + KDevQmlJsPlugin::KDevQmlJsPlugin(QObject* parent, const QVariantList& ) : IPlugin(QLatin1String("kdevqmljssupport"), parent ) , ILanguageSupport() , m_highlighting(new QmlJsHighlighting(this)) , m_refactoring(new BasicRefactoring(this)) +, m_modelManager(new ModelManager(this)) { KDEV_USE_EXTENSION_INTERFACE(ILanguageSupport) QmlJS::registerDUChainItems(); CodeCompletionModel* codeCompletion = new QmlJS::CodeCompletionModel(this); new KDevelop::CodeCompletion(this, codeCompletion, name()); auto assistantsManager = core()->languageController()->staticAssistantsManager(); assistantsManager->registerAssistant(StaticAssistant::Ptr(new RenameAssistant(this))); } KDevQmlJsPlugin::~KDevQmlJsPlugin() { QmlJS::unregisterDUChainItems(); } ParseJob* KDevQmlJsPlugin::createParseJob(const IndexedString& url) { return new QmlJsParseJob(url, this); } QString KDevQmlJsPlugin::name() const { return "qml/js"; } ICodeHighlighting* KDevQmlJsPlugin::codeHighlighting() const { return m_highlighting; } BasicRefactoring* KDevQmlJsPlugin::refactoring() const { return m_refactoring; } ContextMenuExtension KDevQmlJsPlugin::contextMenuExtension(Context* context) { ContextMenuExtension cm; EditorContext *ec = dynamic_cast(context); if (ec && ICore::self()->languageController()->languagesForUrl(ec->url()).contains(this)) { // It's a QML/JS file, let's add our context menu. m_refactoring->fillContextMenu(cm, context); } return cm; } const QString textFromDoc(const IDocument* doc, const KTextEditor::Range& range) { return doc->textDocument()->line(range.start().line()).mid(range.start().column(), range.end().column()-range.start().column()); }; // Finds how many spaces the given string has at one end. // direction=+1 -> left end of the string, -1 for right end. int spacesAtCorner(const QString& string, int direction = +1) { int spaces = 0; QString::const_iterator it; for ( it = direction == 1 ? string.begin() : string.end()-1 ; it != string.end(); it += direction ) { if ( ! it->isSpace() ) break; spaces += 1; } return spaces; } // Take the given QML line and check if it's a line of the form foo.bar: value. // Return ranges for the key and the value. const QPair parseProperty(const QString& line, const KTextEditor::Cursor& position) { QStringList items = line.split(';'); QString matchingItem; int col_offset = -1; // This is to also support FooAnimation { foo: bar; baz: bang; duration: 200 } // or similar foreach ( const QString& item, items ) { col_offset += item.size() + 1; if ( position.column() < col_offset ) { matchingItem = item; break; } } QStringList split = matchingItem.split(':'); if ( split.size() != 2 ) { // The expression is not of the form foo:bar, thus invalid. return qMakePair(KTextEditor::Range::invalid(), KTextEditor::Range::invalid()); } QString key = split.at(0); QString value = split.at(1); // For animations or similar, remove the trailing '}' if there's no semicolon after the last entry if ( value.trimmed().endsWith('}') ) { col_offset -= value.size() - value.lastIndexOf('}') + 1; value = value.left(value.lastIndexOf('}')-1); } return qMakePair( KTextEditor::Range( KTextEditor::Cursor(position.line(), col_offset - value.size() - key.size() + spacesAtCorner(key, +1) - 1), KTextEditor::Cursor(position.line(), col_offset - value.size() - 1 + spacesAtCorner(key, -1)) ), KTextEditor::Range( KTextEditor::Cursor(position.line(), col_offset - value.size() + spacesAtCorner(value, +1)), KTextEditor::Cursor(position.line(), col_offset + spacesAtCorner(value, -1)) )); }; QWidget* KDevQmlJsPlugin::specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) { IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if ( doc && doc->textDocument() ) { // Check for a QML property, and construct a property preview widget // if the property key is listed in the supported properties. QPair property = parseProperty(doc->textDocument()->line(position.line()), position); if ( property.first.isValid() && property.second.isValid() ) { Declaration* decl = DUChainUtils::itemUnderCursor(url, property.first.start()); return PropertyPreviewWidget::constructIfPossible( doc->textDocument(), property.first, property.second, decl, textFromDoc(doc, property.first), textFromDoc(doc, property.second) ); } } // Otherwise, display no special "navigation" widget. return KDevelop::ILanguageSupport::specialLanguageObjectNavigationWidget(url, position); } #include "kdevqmljsplugin.moc" diff --git a/languages/qmljs/kdevqmljsplugin.h b/languages/qmljs/kdevqmljsplugin.h index 3b0a419901..329b292d2a 100644 --- a/languages/qmljs/kdevqmljsplugin.h +++ b/languages/qmljs/kdevqmljsplugin.h @@ -1,48 +1,52 @@ /************************************************************************************* * Copyright (C) 2012 by Aleix Pol * * Copyright (C) 2012 by Milian Wolff * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #ifndef KDEVQMLJSPLUGIN_H #define KDEVQMLJSPLUGIN_H #include #include +class ModelManager; + class KDevQmlJsPlugin : public KDevelop::IPlugin, public KDevelop::ILanguageSupport { Q_OBJECT Q_INTERFACES( KDevelop::ILanguageSupport ) public: explicit KDevQmlJsPlugin( QObject* parent, const QVariantList& args = QVariantList() ); virtual ~KDevQmlJsPlugin(); virtual KDevelop::ParseJob* createParseJob(const KDevelop::IndexedString& url) override; virtual QString name() const override; virtual KDevelop::ICodeHighlighting* codeHighlighting() const override; virtual KDevelop::BasicRefactoring* refactoring() const override; virtual KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; virtual QWidget* specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) override; private: KDevelop::ICodeHighlighting* m_highlighting; KDevelop::BasicRefactoring* m_refactoring; + + ModelManager* m_modelManager; }; #endif // KDEVQMLJSPLUGIN_H diff --git a/languages/qmljs/tests/files/qrc_import.qml b/languages/qmljs/tests/files/qrc_import.qml new file mode 100644 index 0000000000..abb8736c97 --- /dev/null +++ b/languages/qmljs/tests/files/qrc_import.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +import "qrc:///foo" + +Item {} diff --git a/languages/qmljs/tests/test_files.cpp b/languages/qmljs/tests/test_files.cpp index 7c3ea809ed..3348ac2a59 100644 --- a/languages/qmljs/tests/test_files.cpp +++ b/languages/qmljs/tests/test_files.cpp @@ -1,139 +1,140 @@ /************************************************************************************* * Copyright (C) 2013 by Milian Wolff * * Copyright (C) 2013 Olivier de Gaalon * * * * 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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * *************************************************************************************/ #include "test_files.h" #include #include #include #include #include #include #include #include "testfilepaths.h" //Include all used json tests, otherwise "Test not found" #include #include #include #include #include // #include "cppjsontests.h" using namespace KDevelop; QTEST_MAIN(TestFiles) void TestFiles::initTestCase() { AutoTestShell::init(); TestCore::initialize(KDevelop::Core::NoUi); DUChain::self()->disablePersistentStorage(); Core::self()->languageController()->backgroundParser()->setDelay(0); CodeRepresentation::setDiskChangesForbidden(true); } void TestFiles::cleanupTestCase() { TestCore::shutdown(); } void TestFiles::testQMLCustomComponent() { // First parse CustomComponent, so that it is visible and can be used // by CustomComponentUser. Then re-parse CustomComponent and assert that // it has been used. parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponent.qml", false); parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponentUser.qml"); parseAndCheck(TEST_FILES_DIR "/custom_component/CustomComponent.qml"); } void TestFiles::testQMLTypes() { parseAndCheck(TEST_FILES_DIR "/qmltypes/AnItem.qml", true); } void TestFiles::testTypeMismatchFalsePositives() { parseAndCheck(TEST_FILES_DIR "/type_mismatch_false_positives/code.js", true); } void TestFiles::testJSUsesBetweenFiles() { parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_definition.js", false); parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_use.js"); parseAndCheck(TEST_FILES_DIR "/js_cross_file_uses/js_variable_definition.js"); } void TestFiles::testNodeJS() { parseAndCheck(TEST_FILES_DIR "/node_modules/module.js", false); // Ensure that module.js is in the DUChain parseAndCheck(TEST_FILES_DIR "/node.js/module2.js", false); parseAndCheck(TEST_FILES_DIR "/node.js/main.js"); } void TestFiles::testFiles_data() { QTest::addColumn("fileName"); const QString testDirPath = TEST_FILES_DIR; QStringList files = QDir(testDirPath).entryList(QStringList() << "*.js" << "*.qml", QDir::Files); foreach (QString file, files) { QTest::newRow(file.toUtf8()) << QString(testDirPath + "/" + file); } } void TestFiles::testFiles() { QFETCH(QString, fileName); parseAndCheck(fileName); } void TestFiles::parseAndCheck(const QString& fileName, bool check) { const IndexedString indexedFileName(fileName); ReferencedTopDUContext top = DUChain::self()->waitForUpdate(indexedFileName, KDevelop::TopDUContext::AllDeclarationsContextsAndUses); while (!ICore::self()->languageController()->backgroundParser()->isIdle()) { QTest::qWait(500); } QVERIFY(top); if (check) { DUChainReadLocker lock; DeclarationValidator validator; top->visit(validator); QVERIFY(validator.testsPassed()); if (!top->problems().isEmpty()) { foreach(auto p, top->problems()) { qDebug() << p; } } if (!QTest::currentDataTag() || strcmp("failparse.js", QTest::currentDataTag()) != 0) { QEXPECT_FAIL("plugins.qml", "not working properly yet", Continue); + QEXPECT_FAIL("qrc_import.qml", "just making sure it does not crash", Continue); QVERIFY(top->problems().isEmpty()); } } } diff --git a/projectbuilders/makebuilder/makebuilderpreferences.cpp b/projectbuilders/makebuilder/makebuilderpreferences.cpp index 7996ea696f..98bf84649c 100644 --- a/projectbuilders/makebuilder/makebuilderpreferences.cpp +++ b/projectbuilders/makebuilder/makebuilderpreferences.cpp @@ -1,92 +1,100 @@ /* KDevelop CMake Support * * Copyright 2006 Matt Rogers * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "makebuilderpreferences.h" #include #include #include "ui_makeconfig.h" #include "makebuilderconfig.h" -MakeBuilderPreferences::MakeBuilderPreferences(KDevelop::IPlugin* plugin, const KDevelop::ProjectConfigOptions& options, QWidget* parent) +using namespace KDevelop; + +MakeBuilderPreferences::MakeBuilderPreferences(IPlugin* plugin, const ProjectConfigOptions& options, QWidget* parent) : ProjectConfigPage(plugin, options, parent) { QVBoxLayout* l = new QVBoxLayout( this ); QWidget* w = new QWidget; m_prefsUi = new Ui::MakeConfig; m_prefsUi->setupUi( w ); - connect(m_prefsUi->makeBinary, &KUrlRequester::textChanged, this, &MakeBuilderPreferences::changed); - connect(m_prefsUi->makeBinary, &KUrlRequester::urlSelected, this, &MakeBuilderPreferences::changed); + connect(m_prefsUi->makeBinary, &KUrlRequester::textChanged, + this, &MakeBuilderPreferences::changed); + connect(m_prefsUi->makeBinary, &KUrlRequester::urlSelected, + this, &MakeBuilderPreferences::changed); + connect(m_prefsUi->configureEnvironment, &EnvironmentConfigureButton::environmentConfigured, + this, &MakeBuilderPreferences::changed); + connect(m_prefsUi->kcfg_environmentProfile, &EnvironmentSelectionWidget::currentProfileChanged, + this, &MakeBuilderPreferences::changed); l->addWidget( w ); m_prefsUi->configureEnvironment->setSelectionWidget( m_prefsUi->kcfg_environmentProfile ); } void MakeBuilderPreferences::reset() { ProjectConfigPage::reset(); QSignalBlocker sigBlock(this); // don't emit changed signal from m_prefsUi->makeBinary m_prefsUi->makeBinary->setText(MakeBuilderSettings::self()->makeBinary()); } void MakeBuilderPreferences::apply() { MakeBuilderSettings::self()->setMakeBinary(m_prefsUi->makeBinary->text()); MakeBuilderSettings::self()->save(); // TODO: is this needed? KConfigDialogManager should end up calling it ProjectConfigPage::apply(); } void MakeBuilderPreferences::defaults() { MakeBuilderSettings::self()->setDefaults(); m_prefsUi->makeBinary->setText(MakeBuilderSettings::self()->makeBinary()); ProjectConfigPage::defaults(); } MakeBuilderPreferences::~MakeBuilderPreferences() { delete m_prefsUi; } QString MakeBuilderPreferences::standardMakeComannd() { #ifdef _MSC_VER - return QLatin1String("nmake"); + return QStringLiteral("nmake"); #else - return QLatin1String("make"); + return QStringLiteral("make"); #endif } QString MakeBuilderPreferences::name() const { return i18n("Make"); } QString MakeBuilderPreferences::fullName() const { return i18n("Configure Make settings"); } QIcon MakeBuilderPreferences::icon() const { - return QIcon::fromTheme("run-build"); + return QIcon::fromTheme(QStringLiteral("run-build")); } diff --git a/projectbuilders/ninjabuilder/ninjabuilderpreferences.cpp b/projectbuilders/ninjabuilder/ninjabuilderpreferences.cpp index 86e3720e2c..fce440dc6f 100644 --- a/projectbuilders/ninjabuilder/ninjabuilderpreferences.cpp +++ b/projectbuilders/ninjabuilder/ninjabuilderpreferences.cpp @@ -1,64 +1,72 @@ /* KDevelop CMake Support * * Copyright 2006 Matt Rogers * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "ninjabuilderpreferences.h" #include #include #include #include #include #include "ui_ninjaconfig.h" #include "ninjabuilderconfig.h" -NinjaBuilderPreferences::NinjaBuilderPreferences(KDevelop::IPlugin* plugin, - const KDevelop::ProjectConfigOptions& options, QWidget* parent) +using namespace KDevelop; + +NinjaBuilderPreferences::NinjaBuilderPreferences(IPlugin* plugin, + const ProjectConfigOptions& options, + QWidget* parent) : ProjectConfigPage(plugin, options, parent) { QVBoxLayout* l = new QVBoxLayout( this ); QWidget* w = new QWidget; m_prefsUi = new Ui::NinjaConfig; m_prefsUi->setupUi( w ); l->addWidget( w ); + connect(m_prefsUi->configureEnvironment, &EnvironmentConfigureButton::environmentConfigured, + this, &NinjaBuilderPreferences::changed); + connect(m_prefsUi->kcfg_environmentProfile, &EnvironmentSelectionWidget::currentProfileChanged, + this, &NinjaBuilderPreferences::changed); + m_prefsUi->configureEnvironment->setSelectionWidget( m_prefsUi->kcfg_environmentProfile ); } NinjaBuilderPreferences::~NinjaBuilderPreferences() { delete m_prefsUi; } QString NinjaBuilderPreferences::name() const { return i18n("Ninja"); } QString NinjaBuilderPreferences::fullName() const { return i18n("Configure Ninja settings"); } QIcon NinjaBuilderPreferences::icon() const { - return QIcon::fromTheme("run-build"); + return QIcon::fromTheme(QStringLiteral("run-build")); } diff --git a/projectbuilders/qmakebuilder/qmakejob.cpp b/projectbuilders/qmakebuilder/qmakejob.cpp index 1f63ae7e4e..c12241e370 100644 --- a/projectbuilders/qmakebuilder/qmakejob.cpp +++ b/projectbuilders/qmakebuilder/qmakejob.cpp @@ -1,100 +1,107 @@ /* KDevelop QMake Support * * Copyright 2006-2007 Andreas Pakulat * Copyright 2008 Hamish Rodda * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "qmakejob.h" #include "qmakeconfig.h" #include #include #include #include #include #include #include #include #include +using namespace KDevelop; + QMakeJob::QMakeJob(QObject* parent) : OutputExecuteJob(parent) , m_killed(false) { setCapabilities(Killable); + setFilteringStrategy(OutputModel::CompilerFilter); + setProperties(NeedWorkingDirectory | PortableMessages | DisplayStderr | IsBuilderHint); + setToolTitle(i18n("QMake")); + setStandardToolView(IOutputView::BuildView); + setBehaviours(IOutputView::AllowUserClose | IOutputView::AutoScroll ); } void QMakeJob::start() { if (!m_project) { setError(NoProjectError); setErrorText(i18n("No project specified.")); return emitResult(); } - setStandardToolView(KDevelop::IOutputView::BuildView); - setBehaviours(KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll); - setModel(new KDevelop::OutputModel); - startOutput(); - - QString cmd = QMakeConfig::qmakeBinary(m_project); - m_cmd = new KDevelop::CommandExecutor(cmd, this); - connect(m_cmd, SIGNAL(receivedStandardError(const QStringList&)), model(), SLOT(appendLines(const QStringList&))); - connect(m_cmd, SIGNAL(receivedStandardOutput(const QStringList&)), model(), SLOT(appendLines(const QStringList&))); - m_cmd->setWorkingDirectory(m_project->path().toUrl().toLocalFile()); - connect(m_cmd, SIGNAL(failed(QProcess::ProcessError)), this, SLOT(slotFailed(QProcess::ProcessError))); - connect(m_cmd, SIGNAL(completed(int)), this, SLOT(slotCompleted(int))); - m_cmd->start(); + OutputExecuteJob::start(); +} + +QUrl QMakeJob::workingDirectory() const +{ + return m_project ? m_project->path().toUrl() : QUrl(); +} + +QStringList QMakeJob::commandLine() const +{ + QStringList args; + args << QMakeConfig::qmakeBinary(m_project); + return args; } void QMakeJob::setProject(KDevelop::IProject* project) { m_project = project; if (m_project) setObjectName(i18n("QMake: %1", m_project->name())); } void QMakeJob::slotFailed(QProcess::ProcessError error) { qDebug() << error; if (!m_killed) { setError(ConfigureError); // FIXME need more detail i guess setErrorText(i18n("Configure error")); } emitResult(); } void QMakeJob::slotCompleted(int code) { if (code != 0) { setError(FailedShownError); } emitResult(); } bool QMakeJob::doKill() { m_killed = true; m_cmd->kill(); return true; } diff --git a/projectbuilders/qmakebuilder/qmakejob.h b/projectbuilders/qmakebuilder/qmakejob.h index 01b60d61f3..4707a41c9e 100644 --- a/projectbuilders/qmakebuilder/qmakejob.h +++ b/projectbuilders/qmakebuilder/qmakejob.h @@ -1,69 +1,72 @@ /* KDevelop QMake Support * * Copyright 2006-2007 Andreas Pakulat * Copyright 2008 Hamish Rodda * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef QMAKEJOB_H #define QMAKEJOB_H #include #include namespace KDevelop{ class CommandExecutor; class OutputModel; class IProject; } /** @author Andreas Pakulat @author Hamish Rodda (KJob porting) */ class QMakeJob : public KDevelop::OutputExecuteJob { Q_OBJECT public: QMakeJob(QObject *parent = nullptr); enum ErrorTypes { NoProjectError = UserDefinedError, ConfigureError }; void setProject(KDevelop::IProject* project); virtual void start() override; + QUrl workingDirectory() const override; + QStringList commandLine() const override; + protected: bool doKill() override; private Q_SLOTS: void slotFailed(QProcess::ProcessError); void slotCompleted(int); private: KDevelop::IProject* m_project; KDevelop::CommandExecutor* m_cmd; bool m_killed; }; #endif // QMAKEJOB_H diff --git a/projectmanagers/cmake/settings/cmakepreferences.cpp b/projectmanagers/cmake/settings/cmakepreferences.cpp index 1030f16fd1..3229a3fef4 100644 --- a/projectmanagers/cmake/settings/cmakepreferences.cpp +++ b/projectmanagers/cmake/settings/cmakepreferences.cpp @@ -1,360 +1,363 @@ /* KDevelop CMake Support * * Copyright 2006 Matt Rogers * Copyright 2007-2008 Aleix Pol * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "cmakepreferences.h" #include #include #include #include #include #include #include #include "ui_cmakebuildsettings.h" #include "cmakecachedelegate.h" #include "cmakebuilddirchooser.h" #include "../debug.h" #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; -CMakePreferences::CMakePreferences(KDevelop::IPlugin* plugin, const KDevelop::ProjectConfigOptions& options, QWidget* parent) - : KDevelop::ConfigPage(plugin, nullptr, parent), m_project(options.project), m_currentModel(0) +CMakePreferences::CMakePreferences(IPlugin* plugin, const ProjectConfigOptions& options, QWidget* parent) + : ConfigPage(plugin, nullptr, parent), m_project(options.project), m_currentModel(0) { QVBoxLayout* l = new QVBoxLayout( this ); QWidget* w = new QWidget; m_prefsUi = new Ui::CMakeBuildSettings; m_prefsUi->setupUi( w ); l->addWidget( w ); m_prefsUi->addBuildDir->setIcon(QIcon::fromTheme( "list-add" )); m_prefsUi->removeBuildDir->setIcon(QIcon::fromTheme( "list-remove" )); m_prefsUi->addBuildDir->setText(QString()); m_prefsUi->removeBuildDir->setText(QString()); m_prefsUi->cacheList->setItemDelegate(new CMakeCacheDelegate(m_prefsUi->cacheList)); m_prefsUi->cacheList->setSelectionMode(QAbstractItemView::SingleSelection); m_prefsUi->cacheList->horizontalHeader()->setStretchLastSection(true); m_prefsUi->cacheList->verticalHeader()->hide(); connect(m_prefsUi->buildDirs, static_cast(&KComboBox::currentIndexChanged), this, &CMakePreferences::buildDirChanged); connect(m_prefsUi->showInternal, &QCheckBox::stateChanged, this, &CMakePreferences::showInternal); connect(m_prefsUi->addBuildDir, &QPushButton::pressed, this, &CMakePreferences::createBuildDir); connect(m_prefsUi->removeBuildDir, &QPushButton::pressed, this, &CMakePreferences::removeBuildDir); connect(m_prefsUi->showAdvanced, &QPushButton::toggled, this, &CMakePreferences::showAdvanced); - connect(m_prefsUi->environment, &KDevelop::EnvironmentSelectionWidget::currentProfileChanged, this, static_cast(&CMakePreferences::changed)); + connect(m_prefsUi->environment, &EnvironmentSelectionWidget::currentProfileChanged, + this, &CMakePreferences::changed); + connect(m_prefsUi->configureEnvironment, &EnvironmentConfigureButton::environmentConfigured, + this, &CMakePreferences::changed); showInternal(m_prefsUi->showInternal->checkState()); - m_subprojFolder = KDevelop::Path(options.projectTempFile).parent(); + m_subprojFolder = Path(options.projectTempFile).parent(); qCDebug(CMAKE) << "Source folder: " << m_srcFolder << options.projectTempFile; // foreach(const QVariant &v, args) // { // qCDebug(CMAKE) << "arg: " << v.toString(); // } m_prefsUi->configureEnvironment->setSelectionWidget(m_prefsUi->environment); m_prefsUi->showAdvanced->setChecked(false); showAdvanced(false); reset(); // load the initial values } CMakePreferences::~CMakePreferences() { CMake::removeOverrideBuildDirIndex(m_project); delete m_prefsUi; } void CMakePreferences::reset() { qCDebug(CMAKE) << "********loading"; m_prefsUi->buildDirs->clear(); m_prefsUi->buildDirs->addItems( CMake::allBuildDirs(m_project) ); CMake::removeOverrideBuildDirIndex(m_project); // addItems() triggers buildDirChanged(), compensate for it m_prefsUi->buildDirs->setCurrentIndex( CMake::currentBuildDirIndex(m_project) ); m_prefsUi->environment->setCurrentProfile( CMake::currentEnvironment(m_project) ); m_srcFolder = m_project->path(); m_prefsUi->removeBuildDir->setEnabled(m_prefsUi->buildDirs->count()!=0); // QString cmDir=group.readEntry("CMakeDirectory"); // m_prefsUi->kcfg_cmakeDir->setUrl(QUrl(cmDir)); // qCDebug(CMAKE) << "cmakedir" << cmDir; } void CMakePreferences::apply() { qCDebug(CMAKE) << "*******saving"; // the build directory list is incrementally maintained through createBuildDir() and removeBuildDir(). // We won't rewrite it here based on the data from m_prefsUi->buildDirs. CMake::removeOverrideBuildDirIndex( m_project, true ); // save current selection int savedBuildDir = CMake::currentBuildDirIndex(m_project); if (savedBuildDir < 0) { // no build directory exists: skip any writing to config file as well as configuring return; } CMake::setCurrentEnvironment( m_project, m_prefsUi->environment->currentProfile() ); qCDebug(CMAKE) << "writing to cmake config: using builddir " << CMake::currentBuildDirIndex(m_project); qCDebug(CMAKE) << "writing to cmake config: builddir path " << CMake::currentBuildDir(m_project); qCDebug(CMAKE) << "writing to cmake config: installdir " << CMake::currentInstallDir(m_project); qCDebug(CMAKE) << "writing to cmake config: build type " << CMake::currentBuildType(m_project); qCDebug(CMAKE) << "writing to cmake config: cmake binary " << CMake::currentCMakeBinary(m_project); qCDebug(CMAKE) << "writing to cmake config: environment " << CMake::currentEnvironment(m_project); //We run cmake on the builddir to generate it configure(); } void CMakePreferences::defaults() { // do nothing } void CMakePreferences::configureCacheView() { // Sets up the cache view after model re-creation/reset. // Emits changed(false) because model re-creation probably means // mass programmatical invocation of itemChanged(), which invokes changed(true) - which is not what we want. m_prefsUi->cacheList->setModel(m_currentModel); m_prefsUi->cacheList->hideColumn(1); m_prefsUi->cacheList->hideColumn(3); m_prefsUi->cacheList->hideColumn(4); m_prefsUi->cacheList->horizontalHeader()->resizeSection(0, 200); if( m_currentModel ) { m_prefsUi->cacheList->setEnabled( true ); foreach(const QModelIndex & idx, m_currentModel->persistentIndices()) { m_prefsUi->cacheList->openPersistentEditor(idx); } } else { m_prefsUi->cacheList->setEnabled( false ); } showInternal(m_prefsUi->showInternal->checkState()); } -void CMakePreferences::updateCache(const KDevelop::Path &newBuildDir) +void CMakePreferences::updateCache(const Path &newBuildDir) { const Path file = newBuildDir.isValid() ? Path(newBuildDir, "CMakeCache.txt") : Path(); if(QFile::exists(file.toLocalFile())) { m_currentModel->deleteLater(); m_currentModel=new CMakeCacheModel(this, file); configureCacheView(); connect(m_currentModel, &CMakeCacheModel::itemChanged, this, &CMakePreferences::cacheEdited); connect(m_currentModel, &CMakeCacheModel::modelReset, this, &CMakePreferences::configureCacheView); connect(m_prefsUi->cacheList->selectionModel(), &QItemSelectionModel::currentChanged, this, &CMakePreferences::listSelectionChanged); } else { disconnect(m_prefsUi->cacheList->selectionModel(), &QItemSelectionModel::currentChanged, this, 0); m_currentModel->deleteLater(); m_currentModel=0; configureCacheView(); } if( !m_currentModel ) emit changed(); } void CMakePreferences::listSelectionChanged(const QModelIndex & index, const QModelIndex& ) { qCDebug(CMAKE) << "item " << index << " selected"; QModelIndex idx = index.sibling(index.row(), 3); QModelIndex idxType = index.sibling(index.row(), 1); QString comment=QString("%1. %2") .arg(m_currentModel->itemFromIndex(idxType)->text()) .arg(m_currentModel->itemFromIndex(idx)->text()); m_prefsUi->commentText->setText(comment); } void CMakePreferences::showInternal(int state) { if(!m_currentModel) return; bool showAdv=(state == Qt::Checked); for(int i=0; irowCount(); i++) { bool hidden=m_currentModel->isInternal(i) || (!showAdv && m_currentModel->isAdvanced(i)); m_prefsUi->cacheList->setRowHidden(i, hidden); } } void CMakePreferences::buildDirChanged(int index) { CMake::setOverrideBuildDirIndex( m_project, index ); - const KDevelop::Path buildDir = CMake::currentBuildDir(m_project); + const Path buildDir = CMake::currentBuildDir(m_project); m_prefsUi->environment->setCurrentProfile( CMake::currentEnvironment( m_project ) ); updateCache(buildDir); qCDebug(CMAKE) << "builddir Changed" << buildDir; emit changed(); } void CMakePreferences::cacheUpdated() { - const KDevelop::Path buildDir = CMake::currentBuildDir(m_project); + const Path buildDir = CMake::currentBuildDir(m_project); updateCache(buildDir); qCDebug(CMAKE) << "cache updated for" << buildDir; } void CMakePreferences::createBuildDir() { CMakeBuildDirChooser bdCreator; bdCreator.setSourceFolder( m_srcFolder ); // NOTE: (on removing the trailing slashes) // Generally, we have no clue about how shall a trailing slash look in the current system. // Moreover, the slash may be a part of the filename. // It may be '/' or '\', so maybe should we rely on CMake::allBuildDirs() for returning well-formed pathes? QStringList used = CMake::allBuildDirs( m_project ); bdCreator.setAlreadyUsed(used); - bdCreator.setCMakeBinary(KDevelop::Path(CMake::findExecutable())); + bdCreator.setCMakeBinary(Path(CMake::findExecutable())); if(bdCreator.exec()) { QString newbuilddir = bdCreator.buildFolder().toLocalFile(); m_prefsUi->buildDirs->addItem(newbuilddir); int buildDirCount = m_prefsUi->buildDirs->count(); int addedBuildDirIndex = buildDirCount - 1; m_prefsUi->buildDirs->setCurrentIndex(addedBuildDirIndex); m_prefsUi->removeBuildDir->setEnabled(true); // Initialize the kconfig items with the values from the dialog, this ensures the settings // end up in the config file once the changes are saved qCDebug(CMAKE) << "adding to cmake config: new builddir index" << addedBuildDirIndex; qCDebug(CMAKE) << "adding to cmake config: builddir path " << bdCreator.buildFolder(); qCDebug(CMAKE) << "adding to cmake config: installdir " << bdCreator.installPrefix(); qCDebug(CMAKE) << "adding to cmake config: extra args" << bdCreator.extraArguments(); qCDebug(CMAKE) << "adding to cmake config: build type " << bdCreator.buildType(); qCDebug(CMAKE) << "adding to cmake config: cmake binary " << bdCreator.cmakeBinary(); qCDebug(CMAKE) << "adding to cmake config: environment empty"; CMake::setBuildDirCount( m_project, buildDirCount ); CMake::setCurrentBuildDir( m_project, bdCreator.buildFolder() ); CMake::setCurrentInstallDir( m_project, bdCreator.installPrefix() ); CMake::setCurrentExtraArguments( m_project, bdCreator.extraArguments() ); CMake::setCurrentBuildType( m_project, bdCreator.buildType() ); CMake::setCurrentCMakeBinary( m_project, bdCreator.cmakeBinary() ); CMake::setCurrentEnvironment( m_project, QString() ); qCDebug(CMAKE) << "Emitting changed signal for cmake kcm"; emit changed(); } //TODO: Save it for next runs } void CMakePreferences::removeBuildDir() { int curr=m_prefsUi->buildDirs->currentIndex(); if(curr < 0) return; - KDevelop::Path removedPath = CMake::currentBuildDir( m_project ); + Path removedPath = CMake::currentBuildDir( m_project ); QString removed = removedPath.toLocalFile(); if(QDir(removed).exists()) { KMessageBox::ButtonCode ret = KMessageBox::warningYesNo(this, i18n("The %1 directory is about to be removed in KDevelop's list.\n" "Do you want KDevelop to remove it in the file system as well?", removed)); if(ret == KMessageBox::Yes) { bool correct = KIO::del(removedPath.toUrl())->exec(); if(!correct) KMessageBox::error(this, i18n("Could not remove: %1\n", removed)); } } qCDebug(CMAKE) << "removing from cmake config: using builddir " << curr; qCDebug(CMAKE) << "removing from cmake config: builddir path " << removedPath; qCDebug(CMAKE) << "removing from cmake config: installdir " << CMake::currentInstallDir( m_project ); qCDebug(CMAKE) << "removing from cmake config: extra args" << CMake::currentExtraArguments( m_project ); qCDebug(CMAKE) << "removing from cmake config: buildtype " << CMake::currentBuildType( m_project ); qCDebug(CMAKE) << "removing from cmake config: cmake binary " << CMake::currentCMakeBinary( m_project ); qCDebug(CMAKE) << "removing from cmake config: environment " << CMake::currentEnvironment( m_project ); CMake::removeBuildDirConfig(m_project); m_prefsUi->buildDirs->removeItem( curr ); // this triggers buildDirChanged() if(m_prefsUi->buildDirs->count()==0) m_prefsUi->removeBuildDir->setEnabled(false); emit changed(); } void CMakePreferences::configure() { - KDevelop::IProjectBuilder *b=m_project->buildSystemManager()->builder(); + IProjectBuilder *b=m_project->buildSystemManager()->builder(); KJob* job=b->configure(m_project); if( m_currentModel ) { QVariantMap map = m_currentModel->changedValues(); job->setProperty("extraCMakeCacheValues", map); connect(job, &KJob::finished, m_currentModel, &CMakeCacheModel::reset); } else { connect(job, &KJob::finished, this, &CMakePreferences::cacheUpdated); } - connect(job, &KJob::finished, m_project, &KDevelop::IProject::reloadModel); - KDevelop::ICore::self()->runController()->registerJob(job); + connect(job, &KJob::finished, m_project, &IProject::reloadModel); + ICore::self()->runController()->registerJob(job); } void CMakePreferences::showAdvanced(bool v) { qCDebug(CMAKE) << "toggle pressed: " << v; m_prefsUi->advancedBox->setHidden(!v); } QString CMakePreferences::name() const { return i18n("CMake"); } QString CMakePreferences::fullName() const { return i18n("Configure CMake settings"); } QIcon CMakePreferences::icon() const { return QIcon::fromTheme("cmake"); }