From 8c6a98b5d86a5a4951eb8bc0afe65b1a34b11c73 Mon Sep 17 00:00:00 2001 From: Johan Vikstrom Date: Fri, 9 Aug 2019 07:30:28 +0000 Subject: [PATCH] [AST] No longer visiting CXXMethodDecl bodies created by compiler when method was default created. Summary: Clang generates function bodies and puts them in the AST for default methods if it is defaulted outside the class definition. ` struct A { A &operator=(A &&O); }; A &A::operator=(A &&O) = default; ` This will generate a function body for the `A &A::operator=(A &&O)` and put it in the AST. This body should not be visited if implicit code is not visited as it is implicit. This was causing SemanticHighlighting in clangd to generate duplicate tokens and putting them in weird places. Reviewers: hokein, ilya-biryukov, gribozavr Subscribers: mgorny, jkorous, arphaman, kadircet, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D65938 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@368402 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/RecursiveASTVisitor.h | 8 ++- unittests/Tooling/CMakeLists.txt | 1 + .../CXXMethodDecl.cpp | 58 +++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 unittests/Tooling/RecursiveASTVisitorTests/CXXMethodDecl.cpp diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 918e5b084d..924cf832a8 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -2027,7 +2027,13 @@ bool RecursiveASTVisitor::TraverseFunctionHelper(FunctionDecl *D) { } } - 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 auto *MD = dyn_cast(D)) + VisitBody &= !MD->isDefaulted() || getDerived().shouldVisitImplicitCode(); + + if (VisitBody) { TRY_TO(TraverseStmt(D->getBody())); // Function body. } return true; diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt index 99a3e0f209..c699d41c28 100644 --- a/unittests/Tooling/CMakeLists.txt +++ b/unittests/Tooling/CMakeLists.txt @@ -28,6 +28,7 @@ add_clang_unittest(ToolingTests RecursiveASTVisitorTests/ConstructExpr.cpp RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp RecursiveASTVisitorTests/CXXMemberCall.cpp + RecursiveASTVisitorTests/CXXMethodDecl.cpp RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp RecursiveASTVisitorTests/DeclRefExpr.cpp RecursiveASTVisitorTests/ImplicitCtor.cpp diff --git a/unittests/Tooling/RecursiveASTVisitorTests/CXXMethodDecl.cpp b/unittests/Tooling/RecursiveASTVisitorTests/CXXMethodDecl.cpp new file mode 100644 index 0000000000..6441ea99dd --- /dev/null +++ b/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 -- 2.40.0