From ce4c2232d7599403388a945a53968a8ff5c2cb4c Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Wed, 22 Feb 2017 00:52:00 -0800 Subject: [PATCH] update --- compilation_database_loader.cc | 41 + compilation_database_loader.h | 12 + cxxopts.hpp | 1421 ----------------- full_tests/simple_cross_reference/a.cc | 9 + full_tests/simple_cross_reference/a.h | 6 + full_tests/simple_cross_reference/b.cc | 7 + .../compile_commands.json | 12 + main.cpp => indexer.cpp | 330 +--- indexer.h | 325 ++++ indexer.vcxproj.user | 19 + libclangmm/CompilationDatabase.cc | 11 +- libclangmm/CompilationDatabase.h | 26 +- libclangmm/CompileCommand.cc | 35 +- libclangmm/CompileCommand.h | 19 +- libclangmm/CompileCommands.cc | 11 +- libclangmm/CompileCommands.h | 26 +- libclangmm/Cursor.cc | 2 + libclangmm/Cursor.h | 2 +- libclangmm/TranslationUnit.cc | 21 +- query_db.cc | 157 +- 20 files changed, 619 insertions(+), 1873 deletions(-) create mode 100644 compilation_database_loader.cc create mode 100644 compilation_database_loader.h delete mode 100644 cxxopts.hpp create mode 100644 full_tests/simple_cross_reference/a.cc create mode 100644 full_tests/simple_cross_reference/a.h create mode 100644 full_tests/simple_cross_reference/b.cc create mode 100644 full_tests/simple_cross_reference/compile_commands.json rename main.cpp => indexer.cpp (80%) create mode 100644 indexer.h create mode 100644 indexer.vcxproj.user diff --git a/compilation_database_loader.cc b/compilation_database_loader.cc new file mode 100644 index 00000000..b21134f4 --- /dev/null +++ b/compilation_database_loader.cc @@ -0,0 +1,41 @@ +#include "compilation_database_loader.h" + +#include +#include + +#include "libclangmm/Utility.h" + +std::vector LoadCompilationEntriesFromDirectory(const std::string& project_directory) { + CXCompilationDatabase_Error cx_db_load_error; + CXCompilationDatabase cx_db = clang_CompilationDatabase_fromDirectory(project_directory.c_str(), &cx_db_load_error); + if (cx_db_load_error == CXCompilationDatabase_CanNotLoadDatabase) { + std::cerr << "[FATAL]: Unable to load compile_commands.json located at \"" << project_directory << "\""; + exit(1); + } + + + CXCompileCommands cx_commands = clang_CompilationDatabase_getAllCompileCommands(cx_db); + + unsigned int num_commands = clang_CompileCommands_getSize(cx_commands); + std::vector result; + for (unsigned int i = 0; i < num_commands; i++) { + CXCompileCommand cx_command = clang_CompileCommands_getCommand(cx_commands, i); + CompilationEntry entry; + + entry.directory = clang::ToString(clang_CompileCommand_getDirectory(cx_command)); + entry.filename = clang::ToString(clang_CompileCommand_getFilename(cx_command)); + + unsigned int num_args = clang_CompileCommand_getNumArgs(cx_command); + entry.args.reserve(num_args); + for (unsigned int j = 0; j < num_args; ++j) { + entry.args.push_back(clang::ToString(clang_CompileCommand_getArg(cx_command, j))); + } + + result.push_back(entry); + } + + clang_CompileCommands_dispose(cx_commands); + clang_CompilationDatabase_dispose(cx_db); + + return result; +} \ No newline at end of file diff --git a/compilation_database_loader.h b/compilation_database_loader.h new file mode 100644 index 00000000..d95c26d3 --- /dev/null +++ b/compilation_database_loader.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +struct CompilationEntry { + std::string directory; + std::string filename; + std::vector args; +}; + +std::vector LoadCompilationEntriesFromDirectory(const std::string& project_directory); \ No newline at end of file diff --git a/cxxopts.hpp b/cxxopts.hpp deleted file mode 100644 index c33c06a7..00000000 --- a/cxxopts.hpp +++ /dev/null @@ -1,1421 +0,0 @@ -/* - -Copyright (c) 2014, 2015, 2016 Jarryd Beck - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ - -#ifndef CXX_OPTS_HPP -#define CXX_OPTS_HPP - -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//when we ask cxxopts to use Unicode, help strings are processed using ICU, -//which results in the correct lengths being computed for strings when they -//are formatted for the help output -//it is necessary to make sure that can be found by the -//compiler, and that icu-uc is linked in to the binary. - -#ifdef CXXOPTS_USE_UNICODE -#include - -namespace cxxopts -{ - typedef icu::UnicodeString String; - - inline - String - toLocalString(std::string s) - { - return icu::UnicodeString::fromUTF8(s); - } - - class UnicodeStringIterator : public - std::iterator - { - public: - - UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos) - : s(s) - , i(pos) - { - } - - value_type - operator*() const - { - return s->char32At(i); - } - - bool - operator==(const UnicodeStringIterator& rhs) const - { - return s == rhs.s && i == rhs.i; - } - - bool - operator!=(const UnicodeStringIterator& rhs) const - { - return !(*this == rhs); - } - - UnicodeStringIterator& - operator++() - { - ++i; - return *this; - } - - UnicodeStringIterator - operator+(int32_t v) - { - return UnicodeStringIterator(s, i + v); - } - - private: - const icu::UnicodeString* s; - int32_t i; - }; - - inline - String& - stringAppend(String&s, String a) - { - return s.append(std::move(a)); - } - - inline - String& - stringAppend(String& s, int n, UChar32 c) - { - for (int i = 0; i != n; ++i) - { - s.append(c); - } - - return s; - } - - template - String& - stringAppend(String& s, Iterator begin, Iterator end) - { - while (begin != end) - { - s.append(*begin); - ++begin; - } - - return s; - } - - inline - size_t - stringLength(const String& s) - { - return s.length(); - } - - inline - std::string - toUTF8String(const String& s) - { - std::string result; - s.toUTF8String(result); - - return result; - } - - inline - bool - empty(const String& s) - { - return s.isEmpty(); - } -} - -namespace std -{ - cxxopts::UnicodeStringIterator - begin(const icu::UnicodeString& s) - { - return cxxopts::UnicodeStringIterator(&s, 0); - } - - cxxopts::UnicodeStringIterator - end(const icu::UnicodeString& s) - { - return cxxopts::UnicodeStringIterator(&s, s.length()); - } -} - -//ifdef CXXOPTS_USE_UNICODE -#else - -namespace cxxopts -{ - typedef std::string String; - - template - T - toLocalString(T&& t) - { - return t; - } - - inline - size_t - stringLength(const String& s) - { - return s.length(); - } - - inline - String& - stringAppend(String&s, String a) - { - return s.append(std::move(a)); - } - - inline - String& - stringAppend(String& s, size_t n, char c) - { - return s.append(n, c); - } - - template - String& - stringAppend(String& s, Iterator begin, Iterator end) - { - return s.append(begin, end); - } - - template - std::string - toUTF8String(T&& t) - { - return std::forward(t); - } - - inline - bool - empty(const std::string& s) - { - return s.empty(); - } -} - -//ifdef CXXOPTS_USE_UNICODE -#endif - -namespace cxxopts -{ - class Value : public std::enable_shared_from_this - { - public: - - virtual void - parse(const std::string& text) const = 0; - - virtual void - parse() const = 0; - - virtual bool - has_arg() const = 0; - - virtual bool - has_default() const = 0; - - virtual bool - is_container() const = 0; - - virtual bool - has_implicit() const = 0; - - virtual std::string - get_default_value() const = 0; - - virtual std::string - get_implicit_value() const = 0; - - virtual std::shared_ptr - default_value(const std::string& value) = 0; - - virtual std::shared_ptr - implicit_value(const std::string& value) = 0; - }; - - class OptionException : public std::exception - { - public: - OptionException(const std::string& message) - : m_message(message) - { - } - - virtual const char* - what() const noexcept - { - return m_message.c_str(); - } - - private: - std::string m_message; - }; - - class OptionSpecException : public OptionException - { - public: - - OptionSpecException(const std::string& message) - : OptionException(message) - { - } - }; - - class OptionParseException : public OptionException - { - public: - OptionParseException(const std::string& message) - : OptionException(message) - { - } - }; - - class option_exists_error : public OptionSpecException - { - public: - option_exists_error(const std::string& option) - : OptionSpecException(u8"Option ‘" + option + u8"’ already exists") - { - } - }; - - class invalid_option_format_error : public OptionSpecException - { - public: - invalid_option_format_error(const std::string& format) - : OptionSpecException(u8"Invalid option format ‘" + format + u8"’") - { - } - }; - - class option_not_exists_exception : public OptionParseException - { - public: - option_not_exists_exception(const std::string& option) - : OptionParseException(u8"Option ‘" + option + u8"’ does not exist") - { - } - }; - - class missing_argument_exception : public OptionParseException - { - public: - missing_argument_exception(const std::string& option) - : OptionParseException(u8"Option ‘" + option + u8"’ is missing an argument") - { - } - }; - - class option_requires_argument_exception : public OptionParseException - { - public: - option_requires_argument_exception(const std::string& option) - : OptionParseException(u8"Option ‘" + option + u8"’ requires an argument") - { - } - }; - - class option_not_has_argument_exception : public OptionParseException - { - public: - option_not_has_argument_exception - ( - const std::string& option, - const std::string& arg - ) - : OptionParseException( - u8"Option ‘" + option + u8"’ does not take an argument, but argument‘" - + arg + "’ given") - { - } - }; - - class option_not_present_exception : public OptionParseException - { - public: - option_not_present_exception(const std::string& option) - : OptionParseException(u8"Option ‘" + option + u8"’ not present") - { - } - }; - - class argument_incorrect_type : public OptionParseException - { - public: - argument_incorrect_type - ( - const std::string& arg - ) - : OptionParseException( - u8"Argument ‘" + arg + u8"’ failed to parse" - ) - { - } - }; - - namespace values - { - template - void - parse_value(const std::string& text, T& value) - { - std::istringstream is(text); - if (!(is >> value)) - { - throw argument_incorrect_type(text); - } - - if (is.rdbuf()->in_avail() != 0) - { - throw argument_incorrect_type(text); - } - } - - inline - void - parse_value(const std::string& /*text*/, bool& value) - { - //TODO recognise on, off, yes, no, enable, disable - //so that we can write --long=yes explicitly - value = true; - } - - inline - void - parse_value(const std::string& text, std::string& value) - { - value = text; - } - - template - void - parse_value(const std::string& text, std::vector& value) - { - T v; - parse_value(text, v); - value.push_back(v); - } - - template - struct value_has_arg - { - static constexpr bool value = true; - }; - - template <> - struct value_has_arg - { - static constexpr bool value = false; - }; - - template - struct type_is_container - { - static constexpr bool value = false; - }; - - template - struct type_is_container> - { - static constexpr bool value = true; - }; - - template - class standard_value : public Value - { - public: - standard_value() - : m_result(std::make_shared()) - , m_store(m_result.get()) - { - } - - standard_value(T* t) - : m_store(t) - { - } - - void - parse(const std::string& text) const - { - if (m_implicit && text.empty()) - { - parse_value(m_implicit_value, *m_store); - } - else - { - parse_value(text, *m_store); - } - } - - bool - is_container() const - { - return type_is_container::value; - } - - void - parse() const - { - parse_value(m_default_value, *m_store); - } - - bool - has_arg() const - { - return value_has_arg::value; - } - - bool - has_default() const - { - return m_default; - } - - bool - has_implicit() const - { - return m_implicit; - } - - virtual std::shared_ptr - default_value(const std::string& value){ - m_default = true; - m_default_value = value; - return shared_from_this(); - } - - virtual std::shared_ptr - implicit_value(const std::string& value){ - m_implicit = true; - m_implicit_value = value; - return shared_from_this(); - } - - std::string - get_default_value() const - { - return m_default_value; - } - - std::string - get_implicit_value() const - { - return m_implicit_value; - } - - const T& - get() const - { - if (m_store == nullptr) - { - return *m_result; - } - else - { - return *m_store; - } - } - - protected: - std::shared_ptr m_result; - T* m_store; - bool m_default = false; - std::string m_default_value; - bool m_implicit = false; - std::string m_implicit_value; - }; - } - - template - std::shared_ptr - value() - { - return std::make_shared>(); - } - - template - std::shared_ptr - value(T& t) - { - return std::make_shared>(&t); - } - - class OptionAdder; - - class OptionDetails - { - public: - OptionDetails - ( - const String& description, - std::shared_ptr value - ) - : m_desc(description) - , m_value(value) - , m_count(0) - { - } - - const String& - description() const - { - return m_desc; - } - - bool - has_arg() const - { - return m_value->has_arg(); - } - - void - parse(const std::string& text) - { - m_value->parse(text); - ++m_count; - } - - void - parse_default() - { - m_value->parse(); - } - - int - count() const - { - return m_count; - } - - const Value& value() const { - return *m_value; - } - - template - const T& - as() const - { -#ifdef CXXOPTS_NO_RTTI - return static_cast&>(*m_value).get(); -#else - return dynamic_cast&>(*m_value).get(); -#endif - } - - private: - String m_desc; - std::shared_ptr m_value; - int m_count; - }; - - struct HelpOptionDetails - { - std::string s; - std::string l; - String desc; - bool has_arg; - bool has_default; - std::string default_value; - bool has_implicit; - std::string implicit_value; - std::string arg_help; - bool is_container; - }; - - struct HelpGroupDetails - { - std::string name; - std::string description; - std::vector options; - }; - - class Options - { - public: - - Options(std::string program, std::string help_string = "") - : m_program(std::move(program)) - , m_help_string(toLocalString(std::move(help_string))) - , m_next_positional(m_positional.end()) - { - } - - inline - void - parse(int& argc, char**& argv); - - inline - OptionAdder - add_options(std::string group = ""); - - inline - void - add_option - ( - const std::string& group, - const std::string& s, - const std::string& l, - std::string desc, - std::shared_ptr value, - std::string arg_help - ); - - int - count(const std::string& o) const - { - auto iter = m_options.find(o); - if (iter == m_options.end()) - { - return 0; - } - - return iter->second->count(); - } - - const OptionDetails& - operator[](const std::string& option) const - { - auto iter = m_options.find(option); - - if (iter == m_options.end()) - { - throw option_not_present_exception(option); - } - - return *iter->second; - } - - //parse positional arguments into the given option - inline - void - parse_positional(std::string option); - - inline - void - parse_positional(std::vector options); - - inline - std::string - help(const std::vector& groups = {""}) const; - - inline - const std::vector - groups() const; - - inline - const HelpGroupDetails& - group_help(const std::string& group) const; - - private: - - inline - void - add_one_option - ( - const std::string& option, - std::shared_ptr details - ); - - inline - bool - consume_positional(std::string a); - - inline - void - add_to_option(const std::string& option, const std::string& arg); - - inline - void - parse_option - ( - std::shared_ptr value, - const std::string& name, - const std::string& arg = "" - ); - - inline - void - checked_parse_arg - ( - int argc, - char* argv[], - int& current, - std::shared_ptr value, - const std::string& name - ); - - inline - String - help_one_group(const std::string& group) const; - - std::string m_program; - String m_help_string; - - std::map> m_options; - std::vector m_positional; - std::vector::iterator m_next_positional; - std::unordered_set m_positional_set; - - //mapping from groups to help options - std::map m_help; - }; - - class OptionAdder - { - public: - - OptionAdder(Options& options, std::string group) - : m_options(options), m_group(std::move(group)) - { - } - - inline - OptionAdder& - operator() - ( - const std::string& opts, - const std::string& desc, - std::shared_ptr value - = ::cxxopts::value(), - std::string arg_help = "" - ); - - private: - Options& m_options; - std::string m_group; - }; - -} - -namespace cxxopts -{ - - namespace - { - - constexpr int OPTION_LONGEST = 30; - constexpr int OPTION_DESC_GAP = 2; - - std::basic_regex option_matcher - ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); - - std::basic_regex option_specifier - ("(([[:alnum:]]),)?([[:alnum:]][-_[:alnum:]]+)"); - - String - format_option - ( - const HelpOptionDetails& o - ) - { - auto& s = o.s; - auto& l = o.l; - - String result = " "; - - if (s.size() > 0) - { - result += "-" + toLocalString(s) + ","; - } - else - { - result += " "; - } - - if (l.size() > 0) - { - result += " --" + toLocalString(l); - } - - if (o.has_arg) - { - auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; - - if (o.has_implicit) - { - result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; - } - else - { - result += " " + arg; - } - } - - return result; - } - - String - format_description - ( - const HelpOptionDetails& o, - size_t start, - size_t width - ) - { - auto desc = o.desc; - - if (o.has_default) - { - desc += toLocalString(" (default: " + o.default_value + ")"); - } - - String result; - - auto current = std::begin(desc); - auto startLine = current; - auto lastSpace = current; - - auto size = size_t{}; - - while (current != std::end(desc)) - { - if (*current == ' ') - { - lastSpace = current; - } - - if (size > width) - { - if (lastSpace == startLine) - { - stringAppend(result, startLine, current + 1); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = current + 1; - lastSpace = startLine; - } - else - { - stringAppend(result, startLine, lastSpace); - stringAppend(result, "\n"); - stringAppend(result, start, ' '); - startLine = lastSpace + 1; - } - size = 0; - } - else - { - ++size; - } - - ++current; - } - - //append whatever is left - stringAppend(result, startLine, current); - - return result; - } - } - -OptionAdder -Options::add_options(std::string group) -{ - return OptionAdder(*this, std::move(group)); -} - -OptionAdder& -OptionAdder::operator() -( - const std::string& opts, - const std::string& desc, - std::shared_ptr value, - std::string arg_help -) -{ - std::match_results result; - std::regex_match(opts.c_str(), result, option_specifier); - - if (result.empty()) - { - throw invalid_option_format_error(opts); - } - - const auto& s = result[2]; - const auto& l = result[3]; - - m_options.add_option(m_group, s.str(), l.str(), desc, value, - std::move(arg_help)); - - return *this; -} - -void -Options::parse_option -( - std::shared_ptr value, - const std::string& /*name*/, - const std::string& arg -) -{ - value->parse(arg); -} - -void -Options::checked_parse_arg -( - int argc, - char* argv[], - int& current, - std::shared_ptr value, - const std::string& name -) -{ - if (current + 1 >= argc) - { - if (value->value().has_implicit()) - { - parse_option(value, name, ""); - } - else - { - throw missing_argument_exception(name); - } - } - else - { - if (argv[current + 1][0] == '-' && value->value().has_implicit()) - { - parse_option(value, name, ""); - } - else - { - parse_option(value, name, argv[current + 1]); - ++current; - } - } -} - -void -Options::add_to_option(const std::string& option, const std::string& arg) -{ - auto iter = m_options.find(option); - - if (iter == m_options.end()) - { - throw option_not_exists_exception(option); - } - - parse_option(iter->second, option, arg); -} - -bool -Options::consume_positional(std::string a) -{ - while (m_next_positional != m_positional.end()) - { - auto iter = m_options.find(*m_next_positional); - if (iter != m_options.end()) - { - if (!iter->second->value().is_container()) - { - if (iter->second->count() == 0) - { - add_to_option(*m_next_positional, a); - ++m_next_positional; - return true; - } - else - { - ++m_next_positional; - continue; - } - } - else - { - add_to_option(*m_next_positional, a); - return true; - } - } - ++m_next_positional; - } - - return false; -} - -void -Options::parse_positional(std::string option) -{ - parse_positional(std::vector{option}); -} - -void -Options::parse_positional(std::vector options) -{ - m_positional = std::move(options); - m_next_positional = m_positional.begin(); - - m_positional_set.insert(m_positional.begin(), m_positional.end()); -} - -void -Options::parse(int& argc, char**& argv) -{ - int current = 1; - - int nextKeep = 1; - - bool consume_remaining = false; - - while (current != argc) - { - if (strcmp(argv[current], "--") == 0) - { - consume_remaining = true; - ++current; - break; - } - - std::match_results result; - std::regex_match(argv[current], result, option_matcher); - - if (result.empty()) - { - //not a flag - - //if true is returned here then it was consumed, otherwise it is - //ignored - if (consume_positional(argv[current])) - { - } - else - { - argv[nextKeep] = argv[current]; - ++nextKeep; - } - //if we return from here then it was parsed successfully, so continue - } - else - { - //short or long option? - if (result[4].length() != 0) - { - const std::string& s = result[4]; - - for (std::size_t i = 0; i != s.size(); ++i) - { - std::string name(1, s[i]); - auto iter = m_options.find(name); - - if (iter == m_options.end()) - { - throw option_not_exists_exception(name); - } - - auto value = iter->second; - - //if no argument then just add it - if (!value->has_arg()) - { - parse_option(value, name); - } - else - { - //it must be the last argument - if (i + 1 == s.size()) - { - checked_parse_arg(argc, argv, current, value, name); - } - else if (value->value().has_implicit()) - { - parse_option(value, name, ""); - } - else - { - //error - throw option_requires_argument_exception(name); - } - } - } - } - else if (result[1].length() != 0) - { - const std::string& name = result[1]; - - auto iter = m_options.find(name); - - if (iter == m_options.end()) - { - throw option_not_exists_exception(name); - } - - auto opt = iter->second; - - //equals provided for long option? - if (result[3].length() != 0) - { - //parse the option given - - //but if it doesn't take an argument, this is an error - if (!opt->has_arg()) - { - throw option_not_has_argument_exception(name, result[3]); - } - - parse_option(opt, name, result[3]); - } - else - { - if (opt->has_arg()) - { - //parse the next argument - checked_parse_arg(argc, argv, current, opt, name); - } - else - { - //parse with empty argument - parse_option(opt, name); - } - } - } - - } - - ++current; - } - - for (auto& opt : m_options) - { - auto& detail = opt.second; - auto& value = detail->value(); - - if(!detail->count() && value.has_default()){ - detail->parse_default(); - } - } - - if (consume_remaining) - { - while (current < argc) - { - consume_positional(argv[current]); - ++current; - } - } - - argc = nextKeep; - -} - -void -Options::add_option -( - const std::string& group, - const std::string& s, - const std::string& l, - std::string desc, - std::shared_ptr value, - std::string arg_help -) -{ - auto stringDesc = toLocalString(std::move(desc)); - auto option = std::make_shared(stringDesc, value); - - if (s.size() > 0) - { - add_one_option(s, option); - } - - if (l.size() > 0) - { - add_one_option(l, option); - } - - //add the help details - auto& options = m_help[group]; - - options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, - value->has_arg(), - value->has_default(), value->get_default_value(), - value->has_implicit(), value->get_implicit_value(), - std::move(arg_help), - value->is_container()}); -} - -void -Options::add_one_option -( - const std::string& option, - std::shared_ptr details -) -{ - auto in = m_options.emplace(option, details); - - if (!in.second) - { - throw option_exists_error(option); - } -} - -String -Options::help_one_group(const std::string& g) const -{ - typedef std::vector> OptionHelp; - - auto group = m_help.find(g); - if (group == m_help.end()) - { - return ""; - } - - OptionHelp format; - - size_t longest = 0; - - String result; - - if (!g.empty()) - { - result += toLocalString(" " + g + " options:\n"); - } - - for (const auto& o : group->second.options) - { - if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end()) - { - continue; - } - - auto s = format_option(o); - longest = std::max(longest, stringLength(s)); - format.push_back(std::make_pair(s, String())); - } - - longest = std::min(longest, static_cast(OPTION_LONGEST)); - - //widest allowed description - auto allowed = size_t{76} - longest - OPTION_DESC_GAP; - - auto fiter = format.begin(); - for (const auto& o : group->second.options) - { - if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end()) - { - continue; - } - - auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); - - result += fiter->first; - if (stringLength(fiter->first) > longest) - { - result += '\n'; - result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); - } - else - { - result += toLocalString(std::string(longest + OPTION_DESC_GAP - - stringLength(fiter->first), - ' ')); - } - result += d; - result += '\n'; - - ++fiter; - } - - return result; -} - -std::string -Options::help(const std::vector& groups) const -{ - String result = m_help_string + "\nUsage:\n " + - toLocalString(m_program) + " [OPTION...]"; - - if (m_positional.size() > 0) { - result += " positional parameters"; - } - - result += "\n\n"; - - for (std::size_t i = 0; i < groups.size(); ++i) - { - String const& group_help = help_one_group(groups[i]); - if (empty(group_help)) continue; - result += group_help; - if (i < groups.size() - 1) - { - result += '\n'; - } - } - - return toUTF8String(result); -} - -const std::vector -Options::groups() const -{ - std::vector g; - - std::transform( - m_help.begin(), - m_help.end(), - std::back_inserter(g), - [] (const std::map::value_type& pair) - { - return pair.first; - } - ); - - return g; -} - -const HelpGroupDetails& -Options::group_help(const std::string& group) const -{ - return m_help.at(group); -} - -} - -#if defined(__GNU__) -#pragma GCC diagnostic pop -#endif - -#endif //CXX_OPTS_HPP diff --git a/full_tests/simple_cross_reference/a.cc b/full_tests/simple_cross_reference/a.cc new file mode 100644 index 00000000..fd0f6175 --- /dev/null +++ b/full_tests/simple_cross_reference/a.cc @@ -0,0 +1,9 @@ +#include "a.h" + +namespace { +void LocalA() { + Common(); +} +} // namespace + +void Common() {} \ No newline at end of file diff --git a/full_tests/simple_cross_reference/a.h b/full_tests/simple_cross_reference/a.h new file mode 100644 index 00000000..c5b5775f --- /dev/null +++ b/full_tests/simple_cross_reference/a.h @@ -0,0 +1,6 @@ +#ifndef A_H_ +#define A_H_ + +void Common(); + +#endif // A_H_ \ No newline at end of file diff --git a/full_tests/simple_cross_reference/b.cc b/full_tests/simple_cross_reference/b.cc new file mode 100644 index 00000000..5895b608 --- /dev/null +++ b/full_tests/simple_cross_reference/b.cc @@ -0,0 +1,7 @@ +#include "a.h" + +#if RANDOM_DEFINE +static void LocaB() { + Common(); +} +#endif // RANDOM_DEFINE \ No newline at end of file diff --git a/full_tests/simple_cross_reference/compile_commands.json b/full_tests/simple_cross_reference/compile_commands.json new file mode 100644 index 00000000..8a72ba83 --- /dev/null +++ b/full_tests/simple_cross_reference/compile_commands.json @@ -0,0 +1,12 @@ +[ + { + "directory" : "full_tests/simple_cross_reference", + "command" : "clang -c -o a.o a.cc", + "file" : "full_tests/simple_cross_reference/a.cc" + }, + { + "directory" : "full_tests/simple_cross_reference", + "command" : "clang -DRANDOM_DEFINE -c -o b.o b.cc", + "file" : "full_tests/simple_cross_reference/b.cc" + } +] \ No newline at end of file diff --git a/main.cpp b/indexer.cpp similarity index 80% rename from main.cpp rename to indexer.cpp index be4dfeb4..a30bd3ed 100644 --- a/main.cpp +++ b/indexer.cpp @@ -1,324 +1,4 @@ -#include -#include -#include -#include -#include -#include - -#include "libclangmm/clangmm.h" -#include "libclangmm/Utility.h" - -#include "bitfield.h" -#include "utils.h" -#include "optional.h" - -#include -#include -#include -#include - -struct TypeDef; -struct FuncDef; -struct VarDef; - -using FileId = int64_t; -using namespace std::experimental; - - -// TODO: Move off of this weird wrapper, use struct with custom wrappers -// directly. -BEGIN_BITFIELD_TYPE(Location, uint64_t) - -ADD_BITFIELD_MEMBER(interesting, /*start:*/ 0, /*len:*/ 1); // 2 values -ADD_BITFIELD_MEMBER(file_id, /*start:*/ 1, /*len:*/ 29); // 536,870,912 values -ADD_BITFIELD_MEMBER(line, /*start:*/ 30, /*len:*/ 20); // 1,048,576 values -ADD_BITFIELD_MEMBER(column, /*start:*/ 50, /*len:*/ 14); // 16,384 values - -Location(bool interesting, FileId file_id, uint32_t line, uint32_t column) { - this->interesting = interesting; - this->file_id = file_id; - this->line = line; - this->column = column; -} - -std::string ToString() { - // Output looks like this: - // - // *1:2:3 - // - // * => interesting - // 1 => file id - // 2 => line - // 3 => column - - std::string result; - if (interesting) - result += '*'; - result += std::to_string(file_id); - result += ':'; - result += std::to_string(line); - result += ':'; - result += std::to_string(column); - return result; -} - -// Compare two Locations and check if they are equal. Ignores the value of -// |interesting|. -// operator== doesn't seem to work properly... -bool IsEqualTo(const Location& o) { - // When comparing, ignore the value of |interesting|. - return (wrapper.value >> 1) == (o.wrapper.value >> 1); -} - -Location WithInteresting(bool interesting) { - Location result = *this; - result.interesting = interesting; - return result; -} - -END_BITFIELD_TYPE() - -struct FileDb { - std::unordered_map file_path_to_file_id; - std::unordered_map file_id_to_file_path; - - FileDb() { - // Reserve id 0 for unfound. - file_path_to_file_id[""] = 0; - file_id_to_file_path[0] = ""; - } - - Location Resolve(const CXSourceLocation& cx_loc, bool interesting) { - CXFile file; - unsigned int line, column, offset; - clang_getSpellingLocation(cx_loc, &file, &line, &column, &offset); - - FileId file_id; - if (file != nullptr) { - std::string path = clang::ToString(clang_getFileName(file)); - - auto it = file_path_to_file_id.find(path); - if (it != file_path_to_file_id.end()) { - file_id = it->second; - } - else { - file_id = file_path_to_file_id.size(); - file_path_to_file_id[path] = file_id; - file_id_to_file_path[file_id] = path; - } - } - - return Location(interesting, file_id, line, column); - } - - Location Resolve(const CXIdxLoc& cx_idx_loc, bool interesting) { - CXSourceLocation cx_loc = clang_indexLoc_getCXSourceLocation(cx_idx_loc); - return Resolve(cx_loc, interesting); - } - - Location Resolve(const CXCursor& cx_cursor, bool interesting) { - return Resolve(clang_getCursorLocation(cx_cursor), interesting); - } - - Location Resolve(const clang::Cursor& cursor, bool interesting) { - return Resolve(cursor.cx_cursor, interesting); - } -}; - - -template -struct LocalId { - uint64_t local_id; - - LocalId() : local_id(0) {} // Needed for containers. Do not use directly. - explicit LocalId(uint64_t local_id) : local_id(local_id) {} - - bool operator==(const LocalId& other) { - return local_id == other.local_id; - } -}; - -template -bool operator==(const LocalId& a, const LocalId& b) { - return a.local_id == b.local_id; -} - -using TypeId = LocalId; -using FuncId = LocalId; -using VarId = LocalId; - - -template -struct Ref { - LocalId id; - Location loc; - - Ref(LocalId id, Location loc) : id(id), loc(loc) {} -}; -using TypeRef = Ref; -using FuncRef = Ref; -using VarRef = Ref; - - -// TODO: skip as much forward-processing as possible when |is_system_def| is -// set to false. -// TODO: Either eliminate the defs created as a by-product of cross-referencing, -// or do not emit things we don't have definitions for. - -struct TypeDef { - bool is_system_def = false; - - // General metadata. - TypeId id; - std::string usr; - std::string short_name; - std::string qualified_name; - - // While a class/type can technically have a separate declaration/definition, - // it doesn't really happen in practice. The declaration never contains - // comments or insightful information. The user always wants to jump from - // the declaration to the definition - never the other way around like in - // functions and (less often) variables. - // - // It's also difficult to identify a `class Foo;` statement with the clang - // indexer API (it's doable using cursor AST traversal), so we don't bother - // supporting the feature. - optional definition; - - // If set, then this is the same underlying type as the given value (ie, this - // type comes from a using or typedef statement). - optional alias_of; - - // Immediate parent and immediate derived types. - std::vector parents; - std::vector derived; - - // Types, functions, and variables defined in this type. - std::vector types; - std::vector funcs; - std::vector vars; - - // Every usage, useful for things like renames. - // NOTE: Do not insert directly! Use AddUsage instead. - std::vector uses; - - TypeDef(TypeId id, const std::string& usr) : id(id), usr(usr) { - assert(usr.size() > 0); - //std::cout << "Creating type with usr " << usr << std::endl; - } - - void AddUsage(Location loc, bool insert_if_not_present = true) { - if (is_system_def) - return; - - for (int i = uses.size() - 1; i >= 0; --i) { - if (uses[i].IsEqualTo(loc)) { - if (loc.interesting) - uses[i].interesting = true; - return; - } - } - - if (insert_if_not_present) - uses.push_back(loc); - } -}; - -struct FuncDef { - bool is_system_def = false; - - // General metadata. - FuncId id; - std::string usr; - std::string short_name; - std::string qualified_name; - optional declaration; - optional definition; - - // Type which declares this one (ie, it is a method) - optional declaring_type; - // Method this method overrides. - optional base; - // Methods which directly override this one. - std::vector derived; - - // Local variables defined in this function. - std::vector locals; - - // Functions which call this one. - // TODO: Functions can get called outside of just functions - for example, - // they can get called in static context (maybe redirect to main?) - // or in class initializer list (redirect to class ctor?) - // - Right now those usages will not get listed here (but they should be - // inside of all_uses). - std::vector callers; - // Functions that this function calls. - std::vector callees; - - // All usages. For interesting usages, see callees. - std::vector uses; - - FuncDef(FuncId id, const std::string& usr) : id(id), usr(usr) { - assert(usr.size() > 0); - } -}; - -struct VarDef { - bool is_system_def = false; - - // General metadata. - VarId id; - std::string usr; - std::string short_name; - std::string qualified_name; - optional declaration; - // TODO: definitions should be a list of locations, since there can be more - // than one. - optional definition; - - // Type of the variable. - optional variable_type; - - // Type which declares this one (ie, it is a method) - optional declaring_type; - - // Usages. - std::vector uses; - - VarDef(VarId id, const std::string& usr) : id(id), usr(usr) { - assert(usr.size() > 0); - } -}; - - -struct ParsingDatabase { - // NOTE: Every Id is resolved to a file_id of 0. The correct file_id needs - // to get fixed up when inserting into the real db. - std::unordered_map usr_to_type_id; - std::unordered_map usr_to_func_id; - std::unordered_map usr_to_var_id; - - std::vector types; - std::vector funcs; - std::vector vars; - - FileDb file_db; - - ParsingDatabase(); - - TypeId ToTypeId(const std::string& usr); - FuncId ToFuncId(const std::string& usr); - VarId ToVarId(const std::string& usr); - TypeId ToTypeId(const CXCursor& usr); - FuncId ToFuncId(const CXCursor& usr); - VarId ToVarId(const CXCursor& usr); - - TypeDef* Resolve(TypeId id); - FuncDef* Resolve(FuncId id); - VarDef* Resolve(VarId id); - - std::string ToString(); -}; +#include "indexer.h" ParsingDatabase::ParsingDatabase() {} @@ -1284,9 +964,7 @@ void indexEntityReference(CXClientData client_data, const CXIdxEntityRefInfo* re static bool DUMP_AST = true; -ParsingDatabase Parse(std::string filename) { - std::vector args; - +ParsingDatabase Parse(std::string filename, std::vector args) { clang::Index index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/); clang::TranslationUnit tu(index, filename, args); @@ -1412,6 +1090,8 @@ void WriteToFile(const std::string& filename, const std::string& content) { } int mai2n(int argc, char** argv) { + // TODO: Assert that we need to be on clang >= 3.9.1 + /* ParsingDatabase db = Parse("tests/vars/function_local.cc"); std::cout << std::endl << "== Database ==" << std::endl; @@ -1448,7 +1128,7 @@ int mai2n(int argc, char** argv) { // Run test. std::cout << "[START] " << path << std::endl; - ParsingDatabase db = Parse(path); + ParsingDatabase db = Parse(path, {}); std::string actual_output = db.ToString(); //WriteToFile("output.json", actual_output); diff --git a/indexer.h b/indexer.h new file mode 100644 index 00000000..1dc7a775 --- /dev/null +++ b/indexer.h @@ -0,0 +1,325 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "libclangmm/clangmm.h" +#include "libclangmm/Utility.h" + +#include "bitfield.h" +#include "utils.h" +#include "optional.h" + +#include +#include +#include +#include + +struct TypeDef; +struct FuncDef; +struct VarDef; + +using FileId = int64_t; +using namespace std::experimental; + + +// TODO: Move off of this weird wrapper, use struct with custom wrappers +// directly. +BEGIN_BITFIELD_TYPE(Location, uint64_t) + +ADD_BITFIELD_MEMBER(interesting, /*start:*/ 0, /*len:*/ 1); // 2 values +ADD_BITFIELD_MEMBER(file_id, /*start:*/ 1, /*len:*/ 29); // 536,870,912 values +ADD_BITFIELD_MEMBER(line, /*start:*/ 30, /*len:*/ 20); // 1,048,576 values +ADD_BITFIELD_MEMBER(column, /*start:*/ 50, /*len:*/ 14); // 16,384 values + +Location(bool interesting, FileId file_id, uint32_t line, uint32_t column) { + this->interesting = interesting; + this->file_id = file_id; + this->line = line; + this->column = column; +} + +std::string ToString() { + // Output looks like this: + // + // *1:2:3 + // + // * => interesting + // 1 => file id + // 2 => line + // 3 => column + + std::string result; + if (interesting) + result += '*'; + result += std::to_string(file_id); + result += ':'; + result += std::to_string(line); + result += ':'; + result += std::to_string(column); + return result; +} + +// Compare two Locations and check if they are equal. Ignores the value of +// |interesting|. +// operator== doesn't seem to work properly... +bool IsEqualTo(const Location& o) { + // When comparing, ignore the value of |interesting|. + return (wrapper.value >> 1) == (o.wrapper.value >> 1); +} + +Location WithInteresting(bool interesting) { + Location result = *this; + result.interesting = interesting; + return result; +} + +END_BITFIELD_TYPE() + +struct FileDb { + std::unordered_map file_path_to_file_id; + std::unordered_map file_id_to_file_path; + + FileDb() { + // Reserve id 0 for unfound. + file_path_to_file_id[""] = 0; + file_id_to_file_path[0] = ""; + } + + Location Resolve(const CXSourceLocation& cx_loc, bool interesting) { + CXFile file; + unsigned int line, column, offset; + clang_getSpellingLocation(cx_loc, &file, &line, &column, &offset); + + FileId file_id; + if (file != nullptr) { + std::string path = clang::ToString(clang_getFileName(file)); + + auto it = file_path_to_file_id.find(path); + if (it != file_path_to_file_id.end()) { + file_id = it->second; + } + else { + file_id = file_path_to_file_id.size(); + file_path_to_file_id[path] = file_id; + file_id_to_file_path[file_id] = path; + } + } + + return Location(interesting, file_id, line, column); + } + + Location Resolve(const CXIdxLoc& cx_idx_loc, bool interesting) { + CXSourceLocation cx_loc = clang_indexLoc_getCXSourceLocation(cx_idx_loc); + return Resolve(cx_loc, interesting); + } + + Location Resolve(const CXCursor& cx_cursor, bool interesting) { + return Resolve(clang_getCursorLocation(cx_cursor), interesting); + } + + Location Resolve(const clang::Cursor& cursor, bool interesting) { + return Resolve(cursor.cx_cursor, interesting); + } +}; + + +template +struct LocalId { + uint64_t local_id; + + LocalId() : local_id(0) {} // Needed for containers. Do not use directly. + explicit LocalId(uint64_t local_id) : local_id(local_id) {} + + bool operator==(const LocalId& other) { + return local_id == other.local_id; + } +}; + +template +bool operator==(const LocalId& a, const LocalId& b) { + return a.local_id == b.local_id; +} + +using TypeId = LocalId; +using FuncId = LocalId; +using VarId = LocalId; + + +template +struct Ref { + LocalId id; + Location loc; + + Ref(LocalId id, Location loc) : id(id), loc(loc) {} +}; +using TypeRef = Ref; +using FuncRef = Ref; +using VarRef = Ref; + + +// TODO: skip as much forward-processing as possible when |is_system_def| is +// set to false. +// TODO: Either eliminate the defs created as a by-product of cross-referencing, +// or do not emit things we don't have definitions for. + +struct TypeDef { + bool is_system_def = false; + + // General metadata. + TypeId id; + std::string usr; + std::string short_name; + std::string qualified_name; + + // While a class/type can technically have a separate declaration/definition, + // it doesn't really happen in practice. The declaration never contains + // comments or insightful information. The user always wants to jump from + // the declaration to the definition - never the other way around like in + // functions and (less often) variables. + // + // It's also difficult to identify a `class Foo;` statement with the clang + // indexer API (it's doable using cursor AST traversal), so we don't bother + // supporting the feature. + optional definition; + + // If set, then this is the same underlying type as the given value (ie, this + // type comes from a using or typedef statement). + optional alias_of; + + // Immediate parent and immediate derived types. + std::vector parents; + std::vector derived; + + // Types, functions, and variables defined in this type. + std::vector types; + std::vector funcs; + std::vector vars; + + // Every usage, useful for things like renames. + // NOTE: Do not insert directly! Use AddUsage instead. + std::vector uses; + + TypeDef(TypeId id, const std::string& usr) : id(id), usr(usr) { + assert(usr.size() > 0); + //std::cout << "Creating type with usr " << usr << std::endl; + } + + void AddUsage(Location loc, bool insert_if_not_present = true) { + if (is_system_def) + return; + + for (int i = uses.size() - 1; i >= 0; --i) { + if (uses[i].IsEqualTo(loc)) { + if (loc.interesting) + uses[i].interesting = true; + return; + } + } + + if (insert_if_not_present) + uses.push_back(loc); + } +}; + +struct FuncDef { + bool is_system_def = false; + + // General metadata. + FuncId id; + std::string usr; + std::string short_name; + std::string qualified_name; + optional declaration; + optional definition; + + // Type which declares this one (ie, it is a method) + optional declaring_type; + // Method this method overrides. + optional base; + // Methods which directly override this one. + std::vector derived; + + // Local variables defined in this function. + std::vector locals; + + // Functions which call this one. + // TODO: Functions can get called outside of just functions - for example, + // they can get called in static context (maybe redirect to main?) + // or in class initializer list (redirect to class ctor?) + // - Right now those usages will not get listed here (but they should be + // inside of all_uses). + std::vector callers; + // Functions that this function calls. + std::vector callees; + + // All usages. For interesting usages, see callees. + std::vector uses; + + FuncDef(FuncId id, const std::string& usr) : id(id), usr(usr) { + assert(usr.size() > 0); + } +}; + +struct VarDef { + bool is_system_def = false; + + // General metadata. + VarId id; + std::string usr; + std::string short_name; + std::string qualified_name; + optional declaration; + // TODO: definitions should be a list of locations, since there can be more + // than one. + optional definition; + + // Type of the variable. + optional variable_type; + + // Type which declares this one (ie, it is a method) + optional declaring_type; + + // Usages. + std::vector uses; + + VarDef(VarId id, const std::string& usr) : id(id), usr(usr) { + assert(usr.size() > 0); + } +}; + + +struct ParsingDatabase { + // NOTE: Every Id is resolved to a file_id of 0. The correct file_id needs + // to get fixed up when inserting into the real db. + std::unordered_map usr_to_type_id; + std::unordered_map usr_to_func_id; + std::unordered_map usr_to_var_id; + + std::vector types; + std::vector funcs; + std::vector vars; + + FileDb file_db; + + ParsingDatabase(); + + TypeId ToTypeId(const std::string& usr); + FuncId ToFuncId(const std::string& usr); + VarId ToVarId(const std::string& usr); + TypeId ToTypeId(const CXCursor& usr); + FuncId ToFuncId(const CXCursor& usr); + VarId ToVarId(const CXCursor& usr); + + TypeDef* Resolve(TypeId id); + FuncDef* Resolve(FuncId id); + VarDef* Resolve(VarId id); + + std::string ToString(); +}; + +ParsingDatabase Parse(std::string filename, std::vector args); \ No newline at end of file diff --git a/indexer.vcxproj.user b/indexer.vcxproj.user new file mode 100644 index 00000000..22eef80f --- /dev/null +++ b/indexer.vcxproj.user @@ -0,0 +1,19 @@ + + + + --project C:\Users\jacob\Desktop\superindex\indexer\full_tests\simple_cross_reference + WindowsLocalDebugger + + + --project C:\Users\jacob\Desktop\superindex\indexer\full_tests\simple_cross_reference + WindowsLocalDebugger + + + --project C:\Users\jacob\Desktop\superindex\indexer\full_tests\simple_cross_reference + WindowsLocalDebugger + + + --project C:\Users\jacob\Desktop\superindex\indexer\full_tests\simple_cross_reference + WindowsLocalDebugger + + \ No newline at end of file diff --git a/libclangmm/CompilationDatabase.cc b/libclangmm/CompilationDatabase.cc index 46b2c83c..b353cac0 100644 --- a/libclangmm/CompilationDatabase.cc +++ b/libclangmm/CompilationDatabase.cc @@ -1,11 +1,14 @@ #include "CompilationDatabase.h" -#include -clang::CompilationDatabase::CompilationDatabase(const std::string &project_path) { +#include + +clang::CompilationDatabase::CompilationDatabase(const std::string& project_path) { CXCompilationDatabase_Error error; cx_db = clang_CompilationDatabase_fromDirectory(project_path.c_str(), &error); - if(error) { - //TODO: compile_commands.json is missing, create it? + if (error == CXCompilationDatabase_CanNotLoadDatabase) { + std::cerr << "[FATAL]: Unable to load compile_commands.json located at \"" + << project_path << "\""; + exit(1); } } diff --git a/libclangmm/CompilationDatabase.h b/libclangmm/CompilationDatabase.h index 37a30538..e6c69915 100644 --- a/libclangmm/CompilationDatabase.h +++ b/libclangmm/CompilationDatabase.h @@ -1,18 +1,20 @@ -#ifndef COMPILATIONDATABASE_H_ -#define COMPILATIONDATABASE_H_ +#pragma once -#include -#include #include +#include namespace clang { - class CompilationDatabase { - public: - explicit CompilationDatabase(const std::string &project_path); - ~CompilationDatabase(); - CXCompilationDatabase cx_db; - }; +struct CompilationCommand { + std::string path; + std::string args; +}; + +class CompilationDatabase { +public: + explicit CompilationDatabase(const std::string &project_path); + ~CompilationDatabase(); + + CXCompilationDatabase cx_db; +}; } - -#endif // COMPILATIONDATABASE_H_ diff --git a/libclangmm/CompileCommand.cc b/libclangmm/CompileCommand.cc index c7e25842..ff23e34b 100644 --- a/libclangmm/CompileCommand.cc +++ b/libclangmm/CompileCommand.cc @@ -2,20 +2,27 @@ #include "CompileCommands.h" #include "Utility.h" -std::string clang::CompileCommand::get_command() { - std::string res; - unsigned N = clang_CompileCommand_getNumArgs(cx_command); - for (unsigned i = 0; i < N; i++) { - res += ToString(clang_CompileCommand_getArg(cx_command, i)); - } - return res; +namespace clang { + +CompileCommand::CompileCommand(const CXCompileCommand& command) + : cx_command(command) {}; + +std::string CompileCommand::get_command() const { + std::string result; + unsigned int num_args = clang_CompileCommand_getNumArgs(cx_command); + for (unsigned int i = 0; i < num_args; i++) + result += ToString(clang_CompileCommand_getArg(cx_command, i)); + + return result; } -std::vector clang::CompileCommand::get_command_as_args() { - unsigned N = clang_CompileCommand_getNumArgs(cx_command); - std::vector res(N); - for (unsigned i = 0; i < N; i++) { - res[i] = ToString(clang_CompileCommand_getArg(cx_command, i)); - } - return res; +std::vector CompileCommand::get_command_as_args() const { + unsigned num_args = clang_CompileCommand_getNumArgs(cx_command); + std::vector result(num_args); + for (unsigned i = 0; i < num_args; i++) + result[i] = ToString(clang_CompileCommand_getArg(cx_command, i)); + + return result; } + +} // namespace clang \ No newline at end of file diff --git a/libclangmm/CompileCommand.h b/libclangmm/CompileCommand.h index 582d2b44..f769d218 100644 --- a/libclangmm/CompileCommand.h +++ b/libclangmm/CompileCommand.h @@ -1,17 +1,16 @@ -#ifndef COMPILECOMMAND_H_ -#define COMPILECOMMAND_H_ +#pragma once + #include #include #include namespace clang { - class CompileCommand { - public: - CompileCommand(const CXCompileCommand& cx_command) : cx_command(cx_command) {}; - std::string get_command(); - std::vector get_command_as_args(); +class CompileCommand { +public: + CompileCommand(const CXCompileCommand& cx_command); + std::string get_command() const; + std::vector get_command_as_args() const; - CXCompileCommand cx_command; - }; + CXCompileCommand cx_command; +}; } -#endif // COMPILECOMMAND_H_ diff --git a/libclangmm/CompileCommands.cc b/libclangmm/CompileCommands.cc index 44fddcb0..5290c18a 100644 --- a/libclangmm/CompileCommands.cc +++ b/libclangmm/CompileCommands.cc @@ -1,10 +1,10 @@ #include "CompileCommands.h" -clang::CompileCommands::CompileCommands(const std::string &filename, CompilationDatabase &db) { +clang::CompileCommands::CompileCommands(const CompilationDatabase& db) { cx_commands = - clang_CompilationDatabase_getCompileCommands(db.cx_db, filename.c_str()); - if(clang_CompileCommands_getSize(cx_commands)==0) - cx_commands = clang_CompilationDatabase_getAllCompileCommands(db.cx_db); + clang_CompilationDatabase_getAllCompileCommands(db.cx_db); + //if (clang_CompileCommands_getSize(cx_commands) == 0) + // cx_commands = clang_CompilationDatabase_getAllCompileCommands(db.cx_db); } clang::CompileCommands::~CompileCommands() { @@ -15,7 +15,8 @@ std::vector clang::CompileCommands::get_commands() { unsigned N = clang_CompileCommands_getSize(cx_commands); std::vector res; for (unsigned i = 0; i < N; i++) { - res.emplace_back(clang_CompileCommands_getCommand(cx_commands, i)); + CXCompileCommand command = clang_CompileCommands_getCommand(cx_commands, i); + res.push_back(command); } return res; } diff --git a/libclangmm/CompileCommands.h b/libclangmm/CompileCommands.h index f822d188..49ac5440 100644 --- a/libclangmm/CompileCommands.h +++ b/libclangmm/CompileCommands.h @@ -1,19 +1,19 @@ -#ifndef COMPILECOMMANDS_H_ -#define COMPILECOMMANDS_H_ -#include "CompilationDatabase.h" -#include "CompileCommand.h" -#include +#pragma once + #include #include +#include + +#include "CompilationDatabase.h" +#include "CompileCommand.h" namespace clang { - class CompileCommands { - public: - CompileCommands(const std::string &filename, CompilationDatabase &db); - std::vector get_commands(); - ~CompileCommands(); +class CompileCommands { +public: + CompileCommands(const CompilationDatabase& db); + std::vector get_commands(); + ~CompileCommands(); - CXCompileCommands cx_commands; - }; + CXCompileCommands cx_commands; +}; } -#endif // COMPILECOMMANDS_H_ diff --git a/libclangmm/Cursor.cc b/libclangmm/Cursor.cc index 81187ddb..b33296d1 100644 --- a/libclangmm/Cursor.cc +++ b/libclangmm/Cursor.cc @@ -211,6 +211,7 @@ std::string Cursor::get_type_description() const { return spelling; } +#if false std::string Cursor::evaluate() const { CXEvalResult eval = clang_Cursor_Evaluate(cx_cursor); @@ -293,6 +294,7 @@ std::string Cursor::evaluate() const { } +#endif std::string Cursor::get_comments() const { Cursor referenced = get_referenced(); diff --git a/libclangmm/Cursor.h b/libclangmm/Cursor.h index 6344982a..6b7b40d2 100644 --- a/libclangmm/Cursor.h +++ b/libclangmm/Cursor.h @@ -67,7 +67,7 @@ public: std::vector get_arguments() const; bool is_valid_kind() const; - std::string evaluate() const; + //std::string evaluate() const; std::string get_type_description() const; std::string get_comments() const; diff --git a/libclangmm/TranslationUnit.cc b/libclangmm/TranslationUnit.cc index 4db9db5b..462243f1 100644 --- a/libclangmm/TranslationUnit.cc +++ b/libclangmm/TranslationUnit.cc @@ -4,6 +4,7 @@ #include "Utility.h" #include #include +#include namespace clang { @@ -20,20 +21,24 @@ TranslationUnit::TranslationUnit(Index &index, const std::string &file_path, files[0].Contents = buffer.c_str(); files[0].Length = buffer.size(); - cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), - args.size(), files, 1, flags); + CXErrorCode error_code = clang_parseTranslationUnit2( + index.cx_index, file_path.c_str(), args.data(), args.size(), files, 1, flags, &cx_tu); + assert(!error_code); } TranslationUnit::TranslationUnit(Index &index, const std::string &file_path, - const std::vector &command_line_args, - unsigned flags) { + const std::vector &command_line_args, unsigned flags) { + + // TODO: only push defines for the time being. Might need to pass more flags. std::vector args; - for (auto &a : command_line_args) { - args.push_back(a.c_str()); + for (const std::string& a : command_line_args) { + if (a.size() >= 2 && a[0] == '-' && a[1] == 'D') + args.push_back(a.c_str()); } - cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), - args.size(), nullptr, 0, flags); + CXErrorCode error_code = clang_parseTranslationUnit2( + index.cx_index, file_path.c_str(), args.data(), args.size(), nullptr, 0, flags, &cx_tu); + assert(!error_code); } TranslationUnit::~TranslationUnit() { diff --git a/query_db.cc b/query_db.cc index 18c8d21f..d7b9018c 100644 --- a/query_db.cc +++ b/query_db.cc @@ -3,10 +3,11 @@ #include #include -#include "cxxopts.hpp" -#include "optional.h" +#include "compilation_database_loader.h" -using FileId = uint64_t; +//#include "cxxopts.hpp" +#include "optional.h" +#include "indexer.h" struct FileDatabase { std::unordered_map filename_to_file_id; @@ -30,10 +31,6 @@ struct File { std::vector defined_symbols; }; -struct TypeDef {}; -struct FuncDef {}; -struct VarDef {}; - struct QueryableEntry { const char* const str; }; @@ -169,36 +166,87 @@ std::istream& operator >> (std::istream& is, Command& obj) { // TODO: allow user to decide some indexer choices, ie, do we define // TODO: may want to run indexer in separate process to avoid indexer/compiler crashes? +std::unordered_map ParseOptions(int argc, char** argv) { + std::unordered_map output; + + std::string previous_arg; + + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + + if (arg[0] != '-') { + if (previous_arg.size() == 0) { + std::cerr << "Invalid arguments; switches must start with -" << std::endl; + exit(1); + } + + output[previous_arg] = arg; + previous_arg = ""; + } + else { + output[arg] = ""; + previous_arg = arg; + } + } + + return output; +} + +bool HasOption(const std::unordered_map& options, const std::string& option) { + return options.find(option) != options.end(); +} + int main(int argc, char** argv) { - // cxxopts throws exceptions... replace/rewrite lib? - try { - cxxopts::Options options("indexer", "C++ indexer powered by libclang"); + std::unordered_map options = ParseOptions(argc, argv); - options.add_options() - ("h,help", "Print help (this output)") - ("list-commands", "Print information about the query commands") - ("p,project", "Path to compile_commands.json. Needed for the server, and optionally by clients if there are multiple servers running.") - ; + if (argc == 1 || options.find("--help") != options.end()) { + std::cout << R"help(clang-indexer help: - // Server options. - options.add_options("server") - ("server", "Flag indicating that the indexer should create an index that can be queried against using separate invocations of this binary (using the --command flag).", cxxopts::value()) - ("cache-dir", "Path to cache index. Database cache will be restored if present. If not present, the index will be in-memory only.") - ("threads", "Number of threads to use for indexing and querying tasks.") - ; + General: + --help Print this help information. + --help-commands + Print all available query commands. + --project Path to compile_commands.json. Needed for the server, and + optionally by clients if there are multiple servers running. + --print-config + Emit all configuration data this executable is using. + - // Client options. - options.add_options("client") - ("c,command", "Execute a query command against the index. See --command-help. Presence of this flag indicates that the indexer is in client mode.", cxxopts::value()) - ("f,file", "File name to run the index query on", cxxopts::value()) - ("l,location", "A symbol location in the active file the query will operate on. Format is line:column, ie, 10:5, for line 10, column 5.", cxxopts::value()) - ("preferred-symbol-location", "When looking up symbols, try to return the either the 'declaration' or the 'definition'. Defaults to 'definition'.", cxxopts::value()) - ; + Server: + --server If present, this binary will run in server mode. The binary + will not return until killed or an exit is requested. The + server computes and caches an index of the entire program + which is then queried by short-lived client processes. A + client is created by running this binary with a --command + flag. + --cache-dir Directory to cache the index and other useful information. If + a previous cache is present, the database will try to reuse + it. If this flag is not present, the database will be + in-memory only. + --threads Number of threads to use for indexing and querying tasks. + This value is optional; a good estimate is computed by + default. - options.parse(argc, argv); + + Client: + --command Execute a query command against the index. See + --command-help for a listing of valid commands and a + description of what they do. Presence of this flag indicates + that the indexer is in client mode; this flag is mutually + exclusive with --server. + --location Location of the query. Some commands require only a file, + other require a line and column as well. Format is + filename[:line:column]. For example, "foobar.cc" and + "foobar.cc:1:10" are valid inputs. + --preferred-symbol-location + When looking up symbols, try to return either the + 'declaration' or the 'definition'. Defaults to 'definition'. +)help"; + exit(0); + } - if (options.count("list-commands")) { - std::cout << R"(Available commands: + if (HasOption(options, "--help-commands")) { + std::cout << R"(Available commands: callees: callers: @@ -228,39 +276,28 @@ int main(int argc, char** argv) { search: Search for a symbol by name. )"; - exit(0); + exit(0); + } + + if (HasOption(options, "--project")) { + std::vector entries = LoadCompilationEntriesFromDirectory(options["--project"]); + + + std::vector dbs; + for (const CompilationEntry& entry : entries) { + std::cout << "Parsing " << entry.filename << std::endl; + ParsingDatabase db = Parse(entry.filename, entry.args); + + dbs.emplace_back(db); + std::cout << db.ToString() << std::endl << std::endl; } - if (argc == 1 || options.count("help")) { - std::cout << options.help({ "", "server", "client" }) << std::endl; - exit(0); - } - } - catch (cxxopts::OptionException exc) { - fail(exc.what()); + std::cin.get(); + exit(0); } - /* - std::string command; - for (int i = 0; i < argc; ++i) { - if (strcmp(argv[i], "--command") == 0) { - if ((i + 1) >= argc) - fail("missing --command type"); - command = argv[i + 1]; - } - } - - if (command == "") - fail("missing --command switch"); - - - - if (command == "query") { - - } - - std::cout << "Running command " << command; - */ + std::cout << "Invalid arguments. Try --help."; + exit(1); return 0; }