diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -62,6 +62,7 @@ Quality.cpp RIFF.cpp Selection.cpp + SemanticSymbolASTCollector.cpp SourceCode.cpp Threading.cpp Trace.cpp diff --git a/clang-tools-extra/clangd/SemanticSymbolASTCollector.h b/clang-tools-extra/clangd/SemanticSymbolASTCollector.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/SemanticSymbolASTCollector.h @@ -0,0 +1,91 @@ +//===--- SemanticSymbolASTCollector.h - Manipulating source code as strings +//-----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Code for collecting semantic symbols from the C++ AST using the +// RecursiveASTVisitor +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICSYMBOLASTCOLLECTOR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SEMANTICSYMBOLASTCOLLECTOR_H + +#include "AST.h" +#include "Headers.h" +#include "Protocol.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Lex/Lexer.h" + +namespace clang { +namespace clangd { + +// ScopeIndex represents the mapping from the scopes list to a type of +// expression. +enum class SemanticScope : int { + VariableDeclaration = 0, + FunctionDeclaration = 1, +}; + +// Holds all information needed for the highlighting. +struct SemanticSymbol { + SemanticSymbol() {} + SemanticSymbol(SemanticScope Scope, Position StartPosition, unsigned int Len) + : Scope(Scope), StartPosition(StartPosition), Len(Len) {} + SemanticScope Scope; + Position StartPosition; + unsigned int Len; +}; + +bool operator==(const SemanticSymbol &Lhs, const SemanticSymbol &Rhs); +bool operator!=(const SemanticSymbol &Lhs, const SemanticSymbol &Rhs); + +// Collects all semantic symbols in an ASTContext. +class SemanticSymbolASTCollector + : public RecursiveASTVisitor { + std::vector Symbols; + const ASTContext * + const SourceManager &SM; + +public: + SemanticSymbolASTCollector(const ASTContext &AST) + : AST(AST), SM(AST.getSourceManager()) {} + + std::vector getSymbols() { return Symbols; } + + bool VisitVarDecl(VarDecl *Var) { + addSymbol(Var, SemanticScope::VariableDeclaration); + return true; + } + + bool VisitFunctionDecl(FunctionDecl *Func) { + addSymbol(Func, SemanticScope::FunctionDeclaration); + return true; + } + +private: + void addSymbol(Decl *D, SemanticScope Scope) { + auto Loc = D->getLocation(); + SemanticSymbol S; + auto LSPLoc = sourceLocToPosition(SM, Loc); + + S.Len = clang::Lexer::MeasureTokenLength(Loc, SM, AST.getLangOpts()); + if (S.Len == 0) { + // Don't add symbols that don't have any length. + return; + } + + S.StartPosition.character = LSPLoc.character; + S.StartPosition.line = LSPLoc.line; + S.Scope = Scope; + + Symbols.push_back(S); + } +}; +} // namespace clangd +} // namespace clang + +#endif \ No newline at end of file diff --git a/clang-tools-extra/clangd/SemanticSymbolASTCollector.cpp b/clang-tools-extra/clangd/SemanticSymbolASTCollector.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/SemanticSymbolASTCollector.cpp @@ -0,0 +1,14 @@ +#include "SemanticSymbolASTCollector.h" + +namespace clang { +namespace clangd { +bool operator==(const SemanticSymbol &Lhs, const SemanticSymbol &Rhs) { + return Lhs.Scope == Rhs.Scope && Lhs.StartPosition == Rhs.StartPosition && + Lhs.Len == Rhs.Len; +} +bool operator!=(const SemanticSymbol &Lhs, const SemanticSymbol &Rhs) { + return !(Lhs == Rhs); +} + +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -53,6 +53,7 @@ RenameTests.cpp RIFFTests.cpp SelectionTests.cpp + SemanticSymbolASTCollectorTests.cpp SerializationTests.cpp SourceCodeTests.cpp SymbolCollectorTests.cpp diff --git a/clang-tools-extra/clangd/unittests/SemanticSymbolASTCollectorTests.cpp b/clang-tools-extra/clangd/unittests/SemanticSymbolASTCollectorTests.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/SemanticSymbolASTCollectorTests.cpp @@ -0,0 +1,64 @@ +//===-- SemanticSymbolASTCollectorTests.cpp - SemanticSymbolASTCollector tests +//------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Annotations.h" +#include "ClangdUnit.h" +#include "Protocol.h" +#include "SemanticSymbolASTCollector.h" +#include "SourceCode.h" +#include "TestTU.h" +#include "llvm/Support/ScopedPrinter.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +using ::testing::ElementsAreArray; + +Position createPosition(int Line, int Character) { + Position Pos; + Pos.character = Character; + Pos.line = Line; + return Pos; +} + +TEST(SemanticSymbolASTCollector, GetBeginningOfIdentifier) { + std::string Preamble = R"cpp( + struct A { + double SomeMember; + }; + void foo(int a) { + auto VeryLongVariableName = 12312; + A aa; + } + )cpp"; + + Annotations TestCase(Preamble); + std::vector CorrectSymbols = std::vector{ + SemanticSymbol(SemanticScope::FunctionDeclaration, createPosition(4, 9), + 3), + SemanticSymbol(SemanticScope::VariableDeclaration, createPosition(4, 17), + 1), + SemanticSymbol(SemanticScope::VariableDeclaration, createPosition(5, 11), + 20), + SemanticSymbol(SemanticScope::VariableDeclaration, createPosition(6, 12), + 2)}; + + auto AST = TestTU::withCode(TestCase.code()).build(); + SemanticSymbolASTCollector Collector(AST.getASTContext()); + Collector.TraverseAST(AST.getASTContext()); + auto Symbols = Collector.getSymbols(); + EXPECT_THAT(Symbols, ElementsAreArray(CorrectSymbols)); +} + +} // namespace +} // namespace clangd +} // namespace clang