diff --git a/src/documentation.cpp b/src/documentation.cpp index a8c5f26..ef1c202 100644 --- a/src/documentation.cpp +++ b/src/documentation.cpp @@ -1,7 +1,7 @@ #include "documentation.hpp" #include -std::string Documentation::CppReference::get_header(const std::string &symbol) noexcept { +std::vector Documentation::CppReference::get_headers(const std::string &symbol) noexcept { // Extracted from http://upload.cppreference.com/mwiki/images/b/b1/cppreference-doc-20190607.zip // Using raw string instead of map to reduce compile time // Also added: @@ -3118,14 +3118,16 @@ std::experimental::filesystem::status_known experimental/filesystem } } } - std::unordered_map map; + std::unordered_multimap map; }; static SymbolToHeader symbol_to_header(symbol_headers); - auto it = symbol_to_header.map.find(symbol); - if(it == symbol_to_header.map.end()) - return std::string(); - return it->second; + + std::vector results; + auto range = symbol_to_header.map.equal_range(symbol); + for(auto it = range.first; it != range.second; ++it) + results.emplace_back(it->second); + return results; } std::string Documentation::CppReference::get_url(const std::string &symbol) noexcept { diff --git a/src/documentation.hpp b/src/documentation.hpp index c9a8b83..ceec3a2 100644 --- a/src/documentation.hpp +++ b/src/documentation.hpp @@ -1,10 +1,11 @@ #pragma once #include +#include namespace Documentation { class CppReference { public: - static std::string get_header(const std::string &symbol) noexcept; + static std::vector get_headers(const std::string &symbol) noexcept; static std::string get_url(const std::string &symbol) noexcept; }; } // namespace Documentation diff --git a/src/source_clang.cpp b/src/source_clang.cpp index 04c8604..a59dd3c 100644 --- a/src/source_clang.cpp +++ b/src/source_clang.cpp @@ -367,6 +367,25 @@ void Source::ClangViewParse::update_diagnostics() { } return false; }; + auto add_include_fixit = [this, &diagnostic, &get_new_include_offsets](bool has_std, bool has_using_std, const std::string &token) { + auto headers = Documentation::CppReference::get_headers(has_std || has_using_std ? "std::" + token : token); + if(headers.empty() && !has_std && has_using_std) + headers = Documentation::CppReference::get_headers(token); + if(!headers.empty()) { + std::string *c_header = nullptr; + std::string *cpp_header = nullptr; + for(auto &header : headers) { + if(!c_header && header.find(".h") != std::string::npos) + c_header = &header; + else if(!cpp_header) + cpp_header = &header; + } + if(c_header && (is_c || !cpp_header)) + diagnostic.fix_its.emplace_back(clangmm::Diagnostic::FixIt{"#include <" + *c_header + ">\n", file_path.string(), get_new_include_offsets()}); + else if(cpp_header && is_cpp) + diagnostic.fix_its.emplace_back(clangmm::Diagnostic::FixIt{"#include <" + *cpp_header + ">\n", file_path.string(), get_new_include_offsets()}); + } + }; if(diagnostic.fix_its.empty() && diagnostic.severity >= clangmm::Diagnostic::Severity::Error && is_token_char(*start)) { auto token_string = get_token(start); for(size_t c = 0; c < clang_tokens->size(); c++) { @@ -379,18 +398,19 @@ void Source::ClangViewParse::update_diagnostics() { if(cursor.get_referenced()) { auto type_description = cursor.get_type_description(); bool has_std = false; - if(type_description.compare(0, 5, "std::") == 0) { - has_std = true; - type_description.erase(0, 5); + if(is_cpp) { + if(type_description.compare(0, 5, "std::") == 0) { + has_std = true; + type_description.erase(0, 5); + } + if(type_description.compare(0, 5, "__1::") == 0) + type_description.erase(0, 5); + auto pos = type_description.find('<'); + if(pos != std::string::npos) + type_description.erase(pos); } - if(type_description.compare(0, 5, "__1::") == 0) - type_description.erase(0, 5); - auto pos = type_description.find('<'); - if(pos != std::string::npos) - type_description.erase(pos); - auto header = Documentation::CppReference::get_header(is_cpp && (has_std || has_using_namespace_std(c)) ? "std::" + type_description : type_description); - if(!header.empty()) - diagnostic.fix_its.emplace_back(clangmm::Diagnostic::FixIt{"#include <" + header + ">\n", file_path.string(), get_new_include_offsets()}); + + add_include_fixit(has_std, is_cpp && has_using_namespace_std(c), type_description); } } if(diagnostic.spelling.compare(0, 17, "unknown type name") == 0 || @@ -401,19 +421,20 @@ void Source::ClangViewParse::update_diagnostics() { diagnostic.spelling.compare(0, 44, "implicit instantiation of undefined template") == 0 || diagnostic.spelling.compare(0, 79, "no viable constructor or deduction guide for deduction of template arguments of") == 0) { bool has_std = false; - if(token_string == "std" && c + 2 < clang_tokens->size() && (*clang_tokens)[c + 2].get_kind() == clangmm::Token::Kind::Identifier) { - token_string = (*clang_tokens)[c + 2].get_spelling(); - has_std = true; + if(is_cpp) { + if(token_string == "std" && c + 2 < clang_tokens->size() && (*clang_tokens)[c + 2].get_kind() == clangmm::Token::Kind::Identifier) { + token_string = (*clang_tokens)[c + 2].get_spelling(); + has_std = true; + } + else if(c >= 2 && + (*clang_tokens)[c - 1].get_kind() == clangmm::Token::Punctuation && + (*clang_tokens)[c - 2].get_kind() == clangmm::Token::Identifier && + (*clang_tokens)[c - 1].get_spelling() == "::" && + (*clang_tokens)[c - 2].get_spelling() == "std") + has_std = true; } - else if(c >= 2 && - (*clang_tokens)[c - 1].get_kind() == clangmm::Token::Punctuation && - (*clang_tokens)[c - 2].get_kind() == clangmm::Token::Identifier && - (*clang_tokens)[c - 1].get_spelling() == "::" && - (*clang_tokens)[c - 2].get_spelling() == "std") - has_std = true; - auto header = Documentation::CppReference::get_header(is_cpp && (has_std || has_using_namespace_std(c)) ? "std::" + token_string : token_string); - if(!header.empty()) - diagnostic.fix_its.emplace_back(clangmm::Diagnostic::FixIt{"#include <" + header + ">\n", file_path.string(), get_new_include_offsets()}); + + add_include_fixit(has_std, is_cpp && has_using_namespace_std(c), token_string); } break; }