mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-04-02 13:12:09 +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/AST/TemplateBase.h"
|
||||||
#include "clang/Basic/SourceLocation.h"
|
#include "clang/Basic/SourceLocation.h"
|
||||||
#include "clang/Basic/SourceManager.h"
|
#include "clang/Basic/SourceManager.h"
|
||||||
|
#include "clang/Basic/Specifiers.h"
|
||||||
#include "clang/Index/USRGeneration.h"
|
#include "clang/Index/USRGeneration.h"
|
||||||
#include "llvm/ADT/Optional.h"
|
#include "llvm/ADT/Optional.h"
|
||||||
#include "llvm/Support/Casting.h"
|
#include "llvm/Support/Casting.h"
|
||||||
@ -41,13 +42,57 @@ getTemplateSpecializationArgLocs(const NamedDecl &ND) {
|
|||||||
// contain TemplateArgumentLoc information.
|
// contain TemplateArgumentLoc information.
|
||||||
return llvm::None;
|
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
|
} // 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) {
|
bool isImplementationDetail(const Decl *D) {
|
||||||
return !isSpelledInSource(D->getLocation(),
|
return !isSpelledInSource(D->getLocation(),
|
||||||
D->getASTContext().getSourceManager());
|
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) {
|
SourceLocation findName(const clang::Decl *D) {
|
||||||
return D->getLocation();
|
return D->getLocation();
|
||||||
}
|
}
|
||||||
|
@ -80,9 +80,23 @@ std::string printType(const QualType QT, const DeclContext & Context);
|
|||||||
/// take in to account using directives etc
|
/// take in to account using directives etc
|
||||||
/// Example: shortenNamespace("ns1::MyClass<ns1::OtherClass>", "ns1")
|
/// Example: shortenNamespace("ns1::MyClass<ns1::OtherClass>", "ns1")
|
||||||
/// --> "MyClass<ns1::OtherClass>"
|
/// --> "MyClass<ns1::OtherClass>"
|
||||||
std::string shortenNamespace(const llvm::StringRef OriginalName,
|
std::string shortenNamespace(const llvm::StringRef OriginalName,
|
||||||
const llvm::StringRef CurrentNamespace);
|
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 clangd
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "ClangdUnit.h"
|
#include "ClangdUnit.h"
|
||||||
#include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
|
#include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
|
||||||
#include "../clang-tidy/ClangTidyModuleRegistry.h"
|
#include "../clang-tidy/ClangTidyModuleRegistry.h"
|
||||||
|
#include "AST.h"
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
#include "Diagnostics.h"
|
#include "Diagnostics.h"
|
||||||
#include "Headers.h"
|
#include "Headers.h"
|
||||||
@ -19,6 +20,7 @@
|
|||||||
#include "index/CanonicalIncludes.h"
|
#include "index/CanonicalIncludes.h"
|
||||||
#include "index/Index.h"
|
#include "index/Index.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
|
#include "clang/AST/Decl.h"
|
||||||
#include "clang/Basic/LangOptions.h"
|
#include "clang/Basic/LangOptions.h"
|
||||||
#include "clang/Basic/SourceManager.h"
|
#include "clang/Basic/SourceManager.h"
|
||||||
#include "clang/Basic/TokenKinds.h"
|
#include "clang/Basic/TokenKinds.h"
|
||||||
@ -70,6 +72,9 @@ public:
|
|||||||
auto &SM = D->getASTContext().getSourceManager();
|
auto &SM = D->getASTContext().getSourceManager();
|
||||||
if (!isInsideMainFile(D->getLocation(), SM))
|
if (!isInsideMainFile(D->getLocation(), SM))
|
||||||
continue;
|
continue;
|
||||||
|
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D))
|
||||||
|
if (isImplicitTemplateInstantiation(ND))
|
||||||
|
continue;
|
||||||
|
|
||||||
// ObjCMethodDecl are not actually top-level decls.
|
// ObjCMethodDecl are not actually top-level decls.
|
||||||
if (isa<ObjCMethodDecl>(D))
|
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
|
} // namespace
|
||||||
|
|
||||||
clang::CodeCompleteOptions CodeCompleteOptions::getClangCompleteOpts() const {
|
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
|
// We only complete symbol's name, which is the same as the name of the
|
||||||
// *primary* template in case of template specializations.
|
// *primary* template in case of template specializations.
|
||||||
if (isExplicitTemplateSpecialization<FunctionDecl>(ND) ||
|
if (isExplicitTemplateSpecialization(&ND))
|
||||||
isExplicitTemplateSpecialization<CXXRecordDecl>(ND) ||
|
|
||||||
isExplicitTemplateSpecialization<VarDecl>(ND))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (InTopLevelScope(ND))
|
if (InTopLevelScope(ND))
|
||||||
|
@ -6,13 +6,16 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "AST.h"
|
||||||
#include "Annotations.h"
|
#include "Annotations.h"
|
||||||
#include "ClangdUnit.h"
|
#include "ClangdUnit.h"
|
||||||
#include "SourceCode.h"
|
#include "SourceCode.h"
|
||||||
#include "TestTU.h"
|
#include "TestTU.h"
|
||||||
|
#include "clang/AST/DeclTemplate.h"
|
||||||
#include "clang/Basic/TokenKinds.h"
|
#include "clang/Basic/TokenKinds.h"
|
||||||
#include "clang/Tooling/Syntax/Tokens.h"
|
#include "clang/Tooling/Syntax/Tokens.h"
|
||||||
#include "llvm/Support/ScopedPrinter.h"
|
#include "llvm/Support/ScopedPrinter.h"
|
||||||
|
#include "gmock/gmock-matchers.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
@ -21,6 +24,8 @@ namespace clangd {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using ::testing::ElementsAre;
|
using ::testing::ElementsAre;
|
||||||
|
using ::testing::ElementsAreArray;
|
||||||
|
using ::testing::AllOf;
|
||||||
|
|
||||||
TEST(ClangdUnitTest, GetBeginningOfIdentifier) {
|
TEST(ClangdUnitTest, GetBeginningOfIdentifier) {
|
||||||
std::string Preamble = R"cpp(
|
std::string Preamble = R"cpp(
|
||||||
@ -72,6 +77,31 @@ MATCHER_P(DeclNamed, Name, "") {
|
|||||||
return false;
|
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) {
|
TEST(ClangdUnitTest, TopLevelDecls) {
|
||||||
TestTU TU;
|
TestTU TU;
|
||||||
TU.HeaderCode = R"(
|
TU.HeaderCode = R"(
|
||||||
@ -103,6 +133,65 @@ TEST(ClangdUnitTest, DoesNotGetIncludedTopDecls) {
|
|||||||
EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main")));
|
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) {
|
TEST(ClangdUnitTest, TokensAfterPreamble) {
|
||||||
TestTU TU;
|
TestTU TU;
|
||||||
TU.AdditionalFiles["foo.h"] = R"(
|
TU.AdditionalFiles["foo.h"] = R"(
|
||||||
|
@ -249,6 +249,12 @@ TEST(SemanticHighlighting, GetsCorrectTokens) {
|
|||||||
|
|
||||||
template<typename $TemplateParameter[[T]]>
|
template<typename $TemplateParameter[[T]]>
|
||||||
void $Function[[foo]]($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"};
|
)cpp"};
|
||||||
for (const auto &TestCase : TestCases) {
|
for (const auto &TestCase : TestCases) {
|
||||||
checkHighlightings(TestCase);
|
checkHighlightings(TestCase);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user