diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -54,7 +54,7 @@ Annotations Test(Code); auto AST = TestTU::withCode(Test.code()).build(); std::vector ActualTokens = getSemanticHighlightings(AST); - EXPECT_THAT(ActualTokens, getExpectedTokens(Test)); + EXPECT_THAT(ActualTokens, getExpectedTokens(Test)) << Code; } // Any annotations in OldCode and NewCode are converted into their corresponding @@ -255,6 +255,15 @@ struct $Class[[Tmpl]] {$TemplateParameter[[T]] $Field[[x]] = 0;}; extern template struct $Class[[Tmpl]]; template struct $Class[[Tmpl]]; + )cpp", + R"cpp( + struct $Class[[B]] {}; + struct $Class[[A]] { + $Class[[B]] $Field[[BB]]; + $Class[[A]] &operator=($Class[[A]] &&$Variable[[O]]); + }; + + $Class[[A]] &$Class[[A]]::operator=($Class[[A]] &&$Variable[[O]]) = default; )cpp"}; for (const auto &TestCase : TestCases) { checkHighlightings(TestCase); diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2027,7 +2027,13 @@ } } - if (D->isThisDeclarationADefinition()) { + bool VisitBody = D->isThisDeclarationADefinition(); + // If a method is set to default outside the class definition the compiler + // generates the method body and adds it to the AST. + if (const CXXMethodDecl *MD = dyn_cast(D)) + VisitBody &= !MD->isDefaulted() || getDerived().shouldVisitImplicitCode(); + + if (VisitBody) { TRY_TO(TraverseStmt(D->getBody())); // Function body. } return true; diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt --- a/clang/unittests/Tooling/CMakeLists.txt +++ b/clang/unittests/Tooling/CMakeLists.txt @@ -28,6 +28,7 @@ RecursiveASTVisitorTests/ConstructExpr.cpp RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp RecursiveASTVisitorTests/CXXMemberCall.cpp + RecursiveASTVisitorTests/CXXMethodDecl.cpp RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp RecursiveASTVisitorTests/DeclRefExpr.cpp RecursiveASTVisitorTests/ImplicitCtor.cpp diff --git a/clang/unittests/Tooling/RecursiveASTVisitorTests/CXXMethodDecl.cpp b/clang/unittests/Tooling/RecursiveASTVisitorTests/CXXMethodDecl.cpp new file mode 100644 --- /dev/null +++ b/clang/unittests/Tooling/RecursiveASTVisitorTests/CXXMethodDecl.cpp @@ -0,0 +1,58 @@ +//=------ unittest/Tooling/RecursiveASTVisitorTests/CXXMethodDecl.cpp ------=// +// +// 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 "TestVisitor.h" +#include "clang/AST/Expr.h" + +using namespace clang; + +namespace { + +class CXXMethodDeclVisitor + : public ExpectedLocationVisitor { +public: + CXXMethodDeclVisitor(bool VisitImplicitCode) + : VisitImplicitCode(VisitImplicitCode) {} + + bool shouldVisitImplicitCode() const { return VisitImplicitCode; } + + bool VisitDeclRefExpr(DeclRefExpr *D) { + Match("declref", D->getLocation()); + return true; + } + bool VisitParmVarDecl(ParmVarDecl *P) { + Match("parm", P->getLocation()); + return true; + } + +private: + bool VisitImplicitCode; +}; + +TEST(RecursiveASTVisitor, CXXMethodDeclNoDefaultBodyVisited) { + for (bool VisitImplCode : {false, true}) { + CXXMethodDeclVisitor Visitor(VisitImplCode); + if (VisitImplCode) + Visitor.ExpectMatch("declref", 8, 28); + else + Visitor.DisallowMatch("declref", 8, 28); + + Visitor.ExpectMatch("parm", 8, 27); + llvm::StringRef Code = R"cpp( + struct B {}; + struct A { + B BB; + A &operator=(A &&O); + }; + + A &A::operator=(A &&O) = default; + )cpp"; + EXPECT_TRUE(Visitor.runOver(Code, CXXMethodDeclVisitor::Lang_CXX11)); + } +} +} // end anonymous namespace