mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-04-01 12:43:47 +00:00
[clangd] Fix implicit template instatiations appearing as topLevelDecls.
Summary: The parser gives implicit template instantiations to the action's HandleTopLevelDecls callback. This makes semantic highlighting highlight these templated functions multiple times. Fixed by filtering on if a Decl is an implicit function/variable/class instantiation. Also added a testcase to semantic highlighting on this. Reviewers: hokein, ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D65510 llvm-svn: 368261
This commit is contained in:
parent
67ea32a007
commit
720d19b175
@ -15,6 +15,7 @@
|
||||
#include "clang/AST/TemplateBase.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "clang/Index/USRGeneration.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
@ -41,13 +42,57 @@ getTemplateSpecializationArgLocs(const NamedDecl &ND) {
|
||||
// contain TemplateArgumentLoc information.
|
||||
return llvm::None;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool isTemplateSpecializationKind(const NamedDecl *D,
|
||||
TemplateSpecializationKind Kind) {
|
||||
if (const auto *TD = dyn_cast<T>(D))
|
||||
return TD->getTemplateSpecializationKind() == Kind;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isTemplateSpecializationKind(const NamedDecl *D,
|
||||
TemplateSpecializationKind Kind) {
|
||||
return isTemplateSpecializationKind<FunctionDecl>(D, Kind) ||
|
||||
isTemplateSpecializationKind<CXXRecordDecl>(D, Kind) ||
|
||||
isTemplateSpecializationKind<VarDecl>(D, Kind);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool isImplicitTemplateInstantiation(const NamedDecl *D) {
|
||||
return isTemplateSpecializationKind(D, TSK_ImplicitInstantiation);
|
||||
}
|
||||
|
||||
bool isExplicitTemplateSpecialization(const NamedDecl *D) {
|
||||
return isTemplateSpecializationKind(D, TSK_ExplicitSpecialization);
|
||||
}
|
||||
|
||||
bool isImplementationDetail(const Decl *D) {
|
||||
return !isSpelledInSource(D->getLocation(),
|
||||
D->getASTContext().getSourceManager());
|
||||
}
|
||||
|
||||
// Returns true if the complete name of decl \p D is spelled in the source code.
|
||||
// This is not the case for:
|
||||
// * symbols formed via macro concatenation, the spelling location will
|
||||
// be "<scratch space>"
|
||||
// * symbols controlled and defined by a compile command-line option
|
||||
// `-DName=foo`, the spelling location will be "<command line>".
|
||||
bool isSpelledInSourceCode(const Decl *D) {
|
||||
const auto &SM = D->getASTContext().getSourceManager();
|
||||
auto Loc = D->getLocation();
|
||||
// FIXME: Revisit the strategy, the heuristic is limitted when handling
|
||||
// macros, we should use the location where the whole definition occurs.
|
||||
if (Loc.isMacroID()) {
|
||||
std::string PrintLoc = SM.getSpellingLoc(Loc).printToString(SM);
|
||||
if (llvm::StringRef(PrintLoc).startswith("<scratch") ||
|
||||
llvm::StringRef(PrintLoc).startswith("<command line>"))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SourceLocation findName(const clang::Decl *D) {
|
||||
return D->getLocation();
|
||||
}
|
||||
|
@ -80,9 +80,23 @@ std::string printType(const QualType QT, const DeclContext & Context);
|
||||
/// take in to account using directives etc
|
||||
/// Example: shortenNamespace("ns1::MyClass<ns1::OtherClass>", "ns1")
|
||||
/// --> "MyClass<ns1::OtherClass>"
|
||||
std::string shortenNamespace(const llvm::StringRef OriginalName,
|
||||
const llvm::StringRef CurrentNamespace);
|
||||
std::string shortenNamespace(const llvm::StringRef OriginalName,
|
||||
const llvm::StringRef CurrentNamespace);
|
||||
|
||||
/// Indicates if \p D is a template instantiation implicitly generated by the
|
||||
/// compiler, e.g.
|
||||
/// template <class T> struct vector {};
|
||||
/// vector<int> v; // 'vector<int>' is an implicit instantiation
|
||||
bool isImplicitTemplateInstantiation(const NamedDecl *D);
|
||||
/// Indicates if \p D is an explicit template specialization, e.g.
|
||||
/// template <class T> struct vector {};
|
||||
/// template <> struct vector<bool> {}; // <-- explicit specialization
|
||||
///
|
||||
/// Note that explicit instantiations are NOT explicit specializations, albeit
|
||||
/// they look similar.
|
||||
/// template struct vector<bool>; // <-- explicit instantiation, NOT an
|
||||
/// explicit specialization.
|
||||
bool isExplicitTemplateSpecialization(const NamedDecl *D);
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "ClangdUnit.h"
|
||||
#include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
|
||||
#include "../clang-tidy/ClangTidyModuleRegistry.h"
|
||||
#include "AST.h"
|
||||
#include "Compiler.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "Headers.h"
|
||||
@ -19,6 +20,7 @@
|
||||
#include "index/CanonicalIncludes.h"
|
||||
#include "index/Index.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/TokenKinds.h"
|
||||
@ -70,6 +72,9 @@ public:
|
||||
auto &SM = D->getASTContext().getSourceManager();
|
||||
if (!isInsideMainFile(D->getLocation(), SM))
|
||||
continue;
|
||||
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
|
||||
if (isImplicitTemplateInstantiation(ND))
|
||||
continue;
|
||||
|
||||
// ObjCMethodDecl are not actually top-level decls.
|
||||
if (isa<ObjCMethodDecl>(D))
|
||||
|
@ -1674,13 +1674,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> bool isExplicitTemplateSpecialization(const NamedDecl &ND) {
|
||||
if (const auto *TD = dyn_cast<T>(&ND))
|
||||
if (TD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
|
||||
@ -1783,9 +1776,7 @@ bool isIndexedForCodeCompletion(const NamedDecl &ND, ASTContext &ASTCtx) {
|
||||
};
|
||||
// We only complete symbol's name, which is the same as the name of the
|
||||
// *primary* template in case of template specializations.
|
||||
if (isExplicitTemplateSpecialization<FunctionDecl>(ND) ||
|
||||
isExplicitTemplateSpecialization<CXXRecordDecl>(ND) ||
|
||||
isExplicitTemplateSpecialization<VarDecl>(ND))
|
||||
if (isExplicitTemplateSpecialization(&ND))
|
||||
return false;
|
||||
|
||||
if (InTopLevelScope(ND))
|
||||
|
@ -6,13 +6,16 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AST.h"
|
||||
#include "Annotations.h"
|
||||
#include "ClangdUnit.h"
|
||||
#include "SourceCode.h"
|
||||
#include "TestTU.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/Basic/TokenKinds.h"
|
||||
#include "clang/Tooling/Syntax/Tokens.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
#include "gmock/gmock-matchers.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
@ -21,6 +24,8 @@ namespace clangd {
|
||||
namespace {
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::ElementsAreArray;
|
||||
using ::testing::AllOf;
|
||||
|
||||
TEST(ClangdUnitTest, GetBeginningOfIdentifier) {
|
||||
std::string Preamble = R"cpp(
|
||||
@ -72,6 +77,31 @@ MATCHER_P(DeclNamed, Name, "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Matches if the Decl has template args equal to ArgName. If the decl is a
|
||||
// NamedDecl and ArgName is an empty string it also matches.
|
||||
MATCHER_P(WithTemplateArgs, ArgName, "") {
|
||||
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(arg)) {
|
||||
if (const auto *Args = FD->getTemplateSpecializationArgs()) {
|
||||
std::string SpecializationArgs;
|
||||
// Without the PrintingPolicy "bool" will be printed as "_Bool".
|
||||
LangOptions LO;
|
||||
PrintingPolicy Policy(LO);
|
||||
Policy.adjustForCPlusPlus();
|
||||
for (const auto Arg : Args->asArray()) {
|
||||
if (SpecializationArgs.size() > 0)
|
||||
SpecializationArgs += ",";
|
||||
SpecializationArgs += Arg.getAsType().getAsString(Policy);
|
||||
}
|
||||
if (Args->size() == 0)
|
||||
return ArgName == SpecializationArgs;
|
||||
return ArgName == "<" + SpecializationArgs + ">";
|
||||
}
|
||||
}
|
||||
if (const NamedDecl *ND = dyn_cast<NamedDecl>(arg))
|
||||
return printTemplateSpecializationArgs(*ND) == ArgName;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST(ClangdUnitTest, TopLevelDecls) {
|
||||
TestTU TU;
|
||||
TU.HeaderCode = R"(
|
||||
@ -103,6 +133,65 @@ TEST(ClangdUnitTest, DoesNotGetIncludedTopDecls) {
|
||||
EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main")));
|
||||
}
|
||||
|
||||
TEST(ClangdUnitTest, DoesNotGetImplicitTemplateTopDecls) {
|
||||
TestTU TU;
|
||||
TU.Code = R"cpp(
|
||||
template<typename T>
|
||||
void f(T) {}
|
||||
void s() {
|
||||
f(10UL);
|
||||
}
|
||||
)cpp";
|
||||
|
||||
auto AST = TU.build();
|
||||
EXPECT_THAT(AST.getLocalTopLevelDecls(),
|
||||
ElementsAre(DeclNamed("f"), DeclNamed("s")));
|
||||
}
|
||||
|
||||
TEST(ClangdUnitTest,
|
||||
GetsExplicitInstantiationAndSpecializationTemplateTopDecls) {
|
||||
TestTU TU;
|
||||
TU.Code = R"cpp(
|
||||
template <typename T>
|
||||
void f(T) {}
|
||||
template<>
|
||||
void f(bool);
|
||||
template void f(double);
|
||||
|
||||
template <class T>
|
||||
struct V {};
|
||||
template<class T>
|
||||
struct V<T*> {};
|
||||
template <>
|
||||
struct V<bool> {};
|
||||
|
||||
template<class T>
|
||||
T foo = T(10);
|
||||
int i = foo<int>;
|
||||
double d = foo<double>;
|
||||
|
||||
template <class T>
|
||||
int foo<T*> = 0;
|
||||
template <>
|
||||
int foo<bool> = 0;
|
||||
)cpp";
|
||||
|
||||
auto AST = TU.build();
|
||||
EXPECT_THAT(
|
||||
AST.getLocalTopLevelDecls(),
|
||||
ElementsAreArray({AllOf(DeclNamed("f"), WithTemplateArgs("")),
|
||||
AllOf(DeclNamed("f"), WithTemplateArgs("<bool>")),
|
||||
AllOf(DeclNamed("f"), WithTemplateArgs("<double>")),
|
||||
AllOf(DeclNamed("V"), WithTemplateArgs("")),
|
||||
AllOf(DeclNamed("V"), WithTemplateArgs("<T *>")),
|
||||
AllOf(DeclNamed("V"), WithTemplateArgs("<bool>")),
|
||||
AllOf(DeclNamed("foo"), WithTemplateArgs("")),
|
||||
AllOf(DeclNamed("i"), WithTemplateArgs("")),
|
||||
AllOf(DeclNamed("d"), WithTemplateArgs("")),
|
||||
AllOf(DeclNamed("foo"), WithTemplateArgs("<>")),
|
||||
AllOf(DeclNamed("foo"), WithTemplateArgs("<bool>"))}));
|
||||
}
|
||||
|
||||
TEST(ClangdUnitTest, TokensAfterPreamble) {
|
||||
TestTU TU;
|
||||
TU.AdditionalFiles["foo.h"] = R"(
|
||||
|
@ -249,6 +249,12 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
|
||||
|
||||
template<typename $TemplateParameter[[T]]>
|
||||
void $Function[[foo]]($TemplateParameter[[T]] ...);
|
||||
)cpp",
|
||||
R"cpp(
|
||||
template <class $TemplateParameter[[T]]>
|
||||
struct $Class[[Tmpl]] {$TemplateParameter[[T]] $Field[[x]] = 0;};
|
||||
extern template struct $Class[[Tmpl]]<float>;
|
||||
template struct $Class[[Tmpl]]<double>;
|
||||
)cpp"};
|
||||
for (const auto &TestCase : TestCases) {
|
||||
checkHighlightings(TestCase);
|
||||
|
Loading…
x
Reference in New Issue
Block a user