Serenity Operating System
1/*
2 * Copyright (c) 2021-2022, Itamar S. <itamar8910@gmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <AK/DeprecatedString.h>
10#include <AK/Function.h>
11#include <AK/Vector.h>
12#include <DevTools/HackStudio/AutoCompleteResponse.h>
13#include <DevTools/HackStudio/LanguageServers/FileDB.h>
14#include <LibCpp/AST.h>
15#include <LibCpp/Parser.h>
16#include <LibCpp/Preprocessor.h>
17#include <LibGUI/TextPosition.h>
18#include <Libraries/LibCodeComprehension/CodeComprehensionEngine.h>
19
20namespace CodeComprehension::Cpp {
21
22using namespace ::Cpp;
23
24class CppComprehensionEngine : public CodeComprehensionEngine {
25public:
26 CppComprehensionEngine(FileDB const& filedb);
27
28 virtual Vector<CodeComprehension::AutocompleteResultEntry> get_suggestions(DeprecatedString const& file, GUI::TextPosition const& autocomplete_position) override;
29 virtual void on_edit(DeprecatedString const& file) override;
30 virtual void file_opened([[maybe_unused]] DeprecatedString const& file) override;
31 virtual Optional<CodeComprehension::ProjectLocation> find_declaration_of(DeprecatedString const& filename, GUI::TextPosition const& identifier_position) override;
32 virtual Optional<FunctionParamsHint> get_function_params_hint(DeprecatedString const&, GUI::TextPosition const&) override;
33 virtual Vector<CodeComprehension::TokenInfo> get_tokens_info(DeprecatedString const& filename) override;
34
35private:
36 struct SymbolName {
37 StringView name;
38 Vector<StringView> scope;
39
40 static SymbolName create(StringView, Vector<StringView>&&);
41 static SymbolName create(StringView);
42 DeprecatedString scope_as_string() const;
43 DeprecatedString to_deprecated_string() const;
44
45 bool operator==(SymbolName const&) const = default;
46 };
47
48 struct Symbol {
49 SymbolName name;
50 NonnullRefPtr<Cpp::Declaration const> declaration;
51
52 // Local symbols are symbols that should not appear in a global symbol search.
53 // For example, a variable that is declared inside a function will have is_local = true.
54 bool is_local { false };
55
56 enum class IsLocal {
57 No,
58 Yes
59 };
60 static Symbol create(StringView name, Vector<StringView> const& scope, NonnullRefPtr<Cpp::Declaration const>, IsLocal is_local);
61 };
62
63 friend Traits<SymbolName>;
64
65 struct DocumentData {
66 DeprecatedString const& filename() const { return m_filename; }
67 DeprecatedString const& text() const { return m_text; }
68 Preprocessor const& preprocessor() const
69 {
70 VERIFY(m_preprocessor);
71 return *m_preprocessor;
72 }
73 Preprocessor& preprocessor()
74 {
75 VERIFY(m_preprocessor);
76 return *m_preprocessor;
77 }
78 Parser const& parser() const
79 {
80 VERIFY(m_parser);
81 return *m_parser;
82 }
83 Parser& parser()
84 {
85 VERIFY(m_parser);
86 return *m_parser;
87 }
88
89 DeprecatedString m_filename;
90 DeprecatedString m_text;
91 OwnPtr<Preprocessor> m_preprocessor;
92 OwnPtr<Parser> m_parser;
93
94 HashMap<SymbolName, Symbol> m_symbols;
95 HashTable<DeprecatedString> m_available_headers;
96 };
97
98 Vector<CodeComprehension::AutocompleteResultEntry> autocomplete_property(DocumentData const&, MemberExpression const&, const DeprecatedString partial_text) const;
99 Vector<AutocompleteResultEntry> autocomplete_name(DocumentData const&, ASTNode const&, DeprecatedString const& partial_text) const;
100 DeprecatedString type_of(DocumentData const&, Expression const&) const;
101 DeprecatedString type_of_property(DocumentData const&, Identifier const&) const;
102 DeprecatedString type_of_variable(Identifier const&) const;
103 bool is_property(ASTNode const&) const;
104 RefPtr<Cpp::Declaration const> find_declaration_of(DocumentData const&, ASTNode const&) const;
105 RefPtr<Cpp::Declaration const> find_declaration_of(DocumentData const&, SymbolName const&) const;
106 RefPtr<Cpp::Declaration const> find_declaration_of(DocumentData const&, const GUI::TextPosition& identifier_position);
107
108 enum class RecurseIntoScopes {
109 No,
110 Yes
111 };
112
113 Vector<Symbol> properties_of_type(DocumentData const& document, DeprecatedString const& type) const;
114 Vector<Symbol> get_child_symbols(ASTNode const&) const;
115 Vector<Symbol> get_child_symbols(ASTNode const&, Vector<StringView> const& scope, Symbol::IsLocal) const;
116
117 DocumentData const* get_document_data(DeprecatedString const& file) const;
118 DocumentData const* get_or_create_document_data(DeprecatedString const& file);
119 void set_document_data(DeprecatedString const& file, OwnPtr<DocumentData>&& data);
120
121 OwnPtr<DocumentData> create_document_data_for(DeprecatedString const& file);
122 DeprecatedString document_path_from_include_path(StringView include_path) const;
123 void update_declared_symbols(DocumentData&);
124 void update_todo_entries(DocumentData&);
125 CodeComprehension::DeclarationType type_of_declaration(Cpp::Declaration const&);
126 Vector<StringView> scope_of_node(ASTNode const&) const;
127 Vector<StringView> scope_of_reference_to_symbol(ASTNode const&) const;
128
129 Optional<CodeComprehension::ProjectLocation> find_preprocessor_definition(DocumentData const&, const GUI::TextPosition&);
130 Optional<Cpp::Preprocessor::Substitution> find_preprocessor_substitution(DocumentData const&, Cpp::Position const&);
131
132 OwnPtr<DocumentData> create_document_data(DeprecatedString text, DeprecatedString const& filename);
133 Optional<Vector<CodeComprehension::AutocompleteResultEntry>> try_autocomplete_property(DocumentData const&, ASTNode const&, Optional<Token> containing_token) const;
134 Optional<Vector<CodeComprehension::AutocompleteResultEntry>> try_autocomplete_name(DocumentData const&, ASTNode const&, Optional<Token> containing_token) const;
135 Optional<Vector<CodeComprehension::AutocompleteResultEntry>> try_autocomplete_include(DocumentData const&, Token include_path_token, Cpp::Position const& cursor_position) const;
136 static bool is_symbol_available(Symbol const&, Vector<StringView> const& current_scope, Vector<StringView> const& reference_scope);
137 Optional<FunctionParamsHint> get_function_params_hint(DocumentData const&, FunctionCall const&, size_t argument_index);
138
139 template<typename Func>
140 void for_each_available_symbol(DocumentData const&, Func) const;
141
142 template<typename Func>
143 void for_each_included_document_recursive(DocumentData const&, Func) const;
144
145 CodeComprehension::TokenInfo::SemanticType get_token_semantic_type(DocumentData const&, Token const&);
146 CodeComprehension::TokenInfo::SemanticType get_semantic_type_for_identifier(DocumentData const&, Position);
147
148 HashMap<DeprecatedString, OwnPtr<DocumentData>> m_documents;
149
150 // A document's path will be in this set if we're currently processing it.
151 // A document is added to this set when we start processing it (e.g because it was #included) and removed when we're done.
152 // We use this to prevent circular #includes from looping indefinitely.
153 HashTable<DeprecatedString> m_unfinished_documents;
154};
155
156template<typename Func>
157void CppComprehensionEngine::for_each_available_symbol(DocumentData const& document, Func func) const
158{
159 for (auto& item : document.m_symbols) {
160 auto decision = func(item.value);
161 if (decision == IterationDecision::Break)
162 return;
163 }
164
165 for_each_included_document_recursive(document, [&](DocumentData const& document) {
166 for (auto& item : document.m_symbols) {
167 auto decision = func(item.value);
168 if (decision == IterationDecision::Break)
169 return IterationDecision::Break;
170 }
171 return IterationDecision::Continue;
172 });
173}
174
175template<typename Func>
176void CppComprehensionEngine::for_each_included_document_recursive(DocumentData const& document, Func func) const
177{
178 for (auto& included_path : document.m_available_headers) {
179 auto* included_document = get_document_data(included_path);
180 if (!included_document)
181 continue;
182 auto decision = func(*included_document);
183 if (decision == IterationDecision::Break)
184 continue;
185 }
186}
187}
188
189namespace AK {
190
191template<>
192struct Traits<CodeComprehension::Cpp::CppComprehensionEngine::SymbolName> : public GenericTraits<CodeComprehension::Cpp::CppComprehensionEngine::SymbolName> {
193 static unsigned hash(CodeComprehension::Cpp::CppComprehensionEngine::SymbolName const& key)
194 {
195 unsigned hash = 0;
196 hash = pair_int_hash(hash, string_hash(key.name.characters_without_null_termination(), key.name.length()));
197 for (auto& scope_part : key.scope) {
198 hash = pair_int_hash(hash, string_hash(scope_part.characters_without_null_termination(), scope_part.length()));
199 }
200 return hash;
201 }
202};
203
204}