/* * Copyright (c) 2021-2022, Itamar S. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace CodeComprehension::Cpp { using namespace ::Cpp; class CppComprehensionEngine : public CodeComprehensionEngine { public: CppComprehensionEngine(FileDB const& filedb); virtual Vector get_suggestions(String const& file, GUI::TextPosition const& autocomplete_position) override; virtual void on_edit(String const& file) override; virtual void file_opened([[maybe_unused]] String const& file) override; virtual Optional find_declaration_of(String const& filename, GUI::TextPosition const& identifier_position) override; virtual Optional get_function_params_hint(String const&, GUI::TextPosition const&) override; virtual Vector get_tokens_info(String const& filename) override; private: struct SymbolName { StringView name; Vector scope; static SymbolName create(StringView, Vector&&); static SymbolName create(StringView); String scope_as_string() const; String to_string() const; bool operator==(SymbolName const&) const = default; }; struct Symbol { SymbolName name; NonnullRefPtr declaration; // Local symbols are symbols that should not appear in a global symbol search. // For example, a variable that is declared inside a function will have is_local = true. bool is_local { false }; enum class IsLocal { No, Yes }; static Symbol create(StringView name, Vector const& scope, NonnullRefPtr, IsLocal is_local); }; friend Traits; struct DocumentData { String const& filename() const { return m_filename; } String const& text() const { return m_text; } Preprocessor const& preprocessor() const { VERIFY(m_preprocessor); return *m_preprocessor; } Preprocessor& preprocessor() { VERIFY(m_preprocessor); return *m_preprocessor; } Parser const& parser() const { VERIFY(m_parser); return *m_parser; } Parser& parser() { VERIFY(m_parser); return *m_parser; } String m_filename; String m_text; OwnPtr m_preprocessor; OwnPtr m_parser; HashMap m_symbols; HashTable m_available_headers; }; Vector autocomplete_property(DocumentData const&, MemberExpression const&, const String partial_text) const; Vector autocomplete_name(DocumentData const&, ASTNode const&, String const& partial_text) const; String type_of(DocumentData const&, Expression const&) const; String type_of_property(DocumentData const&, Identifier const&) const; String type_of_variable(Identifier const&) const; bool is_property(ASTNode const&) const; RefPtr find_declaration_of(DocumentData const&, ASTNode const&) const; RefPtr find_declaration_of(DocumentData const&, SymbolName const&) const; RefPtr find_declaration_of(DocumentData const&, const GUI::TextPosition& identifier_position); enum class RecurseIntoScopes { No, Yes }; Vector properties_of_type(DocumentData const& document, String const& type) const; Vector get_child_symbols(ASTNode const&) const; Vector get_child_symbols(ASTNode const&, Vector const& scope, Symbol::IsLocal) const; DocumentData const* get_document_data(String const& file) const; DocumentData const* get_or_create_document_data(String const& file); void set_document_data(String const& file, OwnPtr&& data); OwnPtr create_document_data_for(String const& file); String document_path_from_include_path(StringView include_path) const; void update_declared_symbols(DocumentData&); void update_todo_entries(DocumentData&); CodeComprehension::DeclarationType type_of_declaration(Cpp::Declaration const&); Vector scope_of_node(ASTNode const&) const; Vector scope_of_reference_to_symbol(ASTNode const&) const; Optional find_preprocessor_definition(DocumentData const&, const GUI::TextPosition&); Optional find_preprocessor_substitution(DocumentData const&, Cpp::Position const&); OwnPtr create_document_data(String text, String const& filename); Optional> try_autocomplete_property(DocumentData const&, ASTNode const&, Optional containing_token) const; Optional> try_autocomplete_name(DocumentData const&, ASTNode const&, Optional containing_token) const; Optional> try_autocomplete_include(DocumentData const&, Token include_path_token, Cpp::Position const& cursor_position) const; static bool is_symbol_available(Symbol const&, Vector const& current_scope, Vector const& reference_scope); Optional get_function_params_hint(DocumentData const&, FunctionCall&, size_t argument_index); template void for_each_available_symbol(DocumentData const&, Func) const; template void for_each_included_document_recursive(DocumentData const&, Func) const; CodeComprehension::TokenInfo::SemanticType get_token_semantic_type(DocumentData const&, Token const&); CodeComprehension::TokenInfo::SemanticType get_semantic_type_for_identifier(DocumentData const&, Position); HashMap> m_documents; // A document's path will be in this set if we're currently processing it. // A document is added to this set when we start processing it (e.g because it was #included) and removed when we're done. // We use this to prevent circular #includes from looping indefinitely. HashTable m_unfinished_documents; }; template void CppComprehensionEngine::for_each_available_symbol(DocumentData const& document, Func func) const { for (auto& item : document.m_symbols) { auto decision = func(item.value); if (decision == IterationDecision::Break) return; } for_each_included_document_recursive(document, [&](DocumentData const& document) { for (auto& item : document.m_symbols) { auto decision = func(item.value); if (decision == IterationDecision::Break) return IterationDecision::Break; } return IterationDecision::Continue; }); } template void CppComprehensionEngine::for_each_included_document_recursive(DocumentData const& document, Func func) const { for (auto& included_path : document.m_available_headers) { auto* included_document = get_document_data(included_path); if (!included_document) continue; auto decision = func(*included_document); if (decision == IterationDecision::Break) continue; } } } namespace AK { template<> struct Traits : public GenericTraits { static unsigned hash(CodeComprehension::Cpp::CppComprehensionEngine::SymbolName const& key) { unsigned hash = 0; hash = pair_int_hash(hash, string_hash(key.name.characters_without_null_termination(), key.name.length())); for (auto& scope_part : key.scope) { hash = pair_int_hash(hash, string_hash(scope_part.characters_without_null_termination(), scope_part.length())); } return hash; } }; }