forked from OSchip/llvm-project
[clangd] Add "inline" keyword to prevent ODR-violations in DefineInline
Reviewers: ilya-biryukov, hokein Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D68261
This commit is contained in:
parent
7c13fe8a6a
commit
087528a331
|
@ -32,6 +32,7 @@
|
|||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/TokenKinds.h"
|
||||
#include "clang/Driver/Types.h"
|
||||
#include "clang/Index/IndexDataConsumer.h"
|
||||
#include "clang/Index/IndexSymbol.h"
|
||||
#include "clang/Index/IndexingAction.h"
|
||||
|
@ -360,6 +361,25 @@ const SourceLocation getBeginLoc(const FunctionDecl *FD) {
|
|||
return FD->getBeginLoc();
|
||||
}
|
||||
|
||||
llvm::Optional<tooling::Replacement>
|
||||
addInlineIfInHeader(const FunctionDecl *FD) {
|
||||
// This includes inline functions and constexpr functions.
|
||||
if (FD->isInlined() || llvm::isa<CXXMethodDecl>(FD))
|
||||
return llvm::None;
|
||||
// Primary template doesn't need inline.
|
||||
if (FD->isTemplated() && !FD->isFunctionTemplateSpecialization())
|
||||
return llvm::None;
|
||||
|
||||
const SourceManager &SM = FD->getASTContext().getSourceManager();
|
||||
llvm::StringRef FileName = SM.getFilename(FD->getLocation());
|
||||
|
||||
// If it is not a header we don't need to mark function as "inline".
|
||||
if (!isHeaderFile(FileName, FD->getASTContext().getLangOpts()))
|
||||
return llvm::None;
|
||||
|
||||
return tooling::Replacement(SM, FD->getInnerLocStart(), 0, "inline ");
|
||||
}
|
||||
|
||||
/// Moves definition of a function/method to its declaration location.
|
||||
/// Before:
|
||||
/// a.h:
|
||||
|
@ -436,6 +456,7 @@ public:
|
|||
"Couldn't find semicolon for target declaration.");
|
||||
}
|
||||
|
||||
auto AddInlineIfNecessary = addInlineIfInHeader(Target);
|
||||
auto ParamReplacements = renameParameters(Target, Source);
|
||||
if (!ParamReplacements)
|
||||
return ParamReplacements.takeError();
|
||||
|
@ -446,6 +467,13 @@ public:
|
|||
|
||||
const tooling::Replacement SemicolonToFuncBody(SM, *Semicolon, 1,
|
||||
*QualifiedBody);
|
||||
tooling::Replacements TargetFileReplacements(SemicolonToFuncBody);
|
||||
TargetFileReplacements = TargetFileReplacements.merge(*ParamReplacements);
|
||||
if (AddInlineIfNecessary) {
|
||||
if (auto Err = TargetFileReplacements.add(*AddInlineIfNecessary))
|
||||
return std::move(Err);
|
||||
}
|
||||
|
||||
auto DefRange = toHalfOpenFileRange(
|
||||
SM, AST.getLangOpts(),
|
||||
SM.getExpansionRange(CharSourceRange::getCharRange(getBeginLoc(Source),
|
||||
|
@ -462,9 +490,8 @@ public:
|
|||
|
||||
llvm::SmallVector<std::pair<std::string, Edit>, 2> Edits;
|
||||
// Edit for Target.
|
||||
auto FE = Effect::fileEdit(
|
||||
SM, SM.getFileID(*Semicolon),
|
||||
tooling::Replacements(SemicolonToFuncBody).merge(*ParamReplacements));
|
||||
auto FE = Effect::fileEdit(SM, SM.getFileID(*Semicolon),
|
||||
std::move(TargetFileReplacements));
|
||||
if (!FE)
|
||||
return FE.takeError();
|
||||
Edits.push_back(std::move(*FE));
|
||||
|
|
|
@ -1864,6 +1864,66 @@ TEST_F(DefineInlineTest, QualifyWithUsingDirectives) {
|
|||
EXPECT_EQ(apply(Test), Expected) << Test;
|
||||
}
|
||||
|
||||
TEST_F(DefineInlineTest, AddInline) {
|
||||
llvm::StringMap<std::string> EditedFiles;
|
||||
ExtraFiles["a.h"] = "void foo();";
|
||||
apply(R"cpp(#include "a.h"
|
||||
void fo^o() {})cpp", &EditedFiles);
|
||||
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
||||
testPath("a.h"), "inline void foo(){}")));
|
||||
|
||||
// Check we put inline before cv-qualifiers.
|
||||
ExtraFiles["a.h"] = "const int foo();";
|
||||
apply(R"cpp(#include "a.h"
|
||||
const int fo^o() {})cpp", &EditedFiles);
|
||||
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
||||
testPath("a.h"), "inline const int foo(){}")));
|
||||
|
||||
// No double inline.
|
||||
ExtraFiles["a.h"] = "inline void foo();";
|
||||
apply(R"cpp(#include "a.h"
|
||||
inline void fo^o() {})cpp", &EditedFiles);
|
||||
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
||||
testPath("a.h"), "inline void foo(){}")));
|
||||
|
||||
// Constexprs don't need "inline".
|
||||
ExtraFiles["a.h"] = "constexpr void foo();";
|
||||
apply(R"cpp(#include "a.h"
|
||||
constexpr void fo^o() {})cpp", &EditedFiles);
|
||||
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
|
||||
testPath("a.h"), "constexpr void foo(){}")));
|
||||
|
||||
// Class members don't need "inline".
|
||||
ExtraFiles["a.h"] = "struct Foo { void foo(); }";
|
||||
apply(R"cpp(#include "a.h"
|
||||
void Foo::fo^o() {})cpp", &EditedFiles);
|
||||
EXPECT_THAT(EditedFiles,
|
||||
testing::ElementsAre(FileWithContents(
|
||||
testPath("a.h"), "struct Foo { void foo(){} }")));
|
||||
|
||||
// Function template doesn't need to be "inline"d.
|
||||
ExtraFiles["a.h"] = "template <typename T> void foo();";
|
||||
apply(R"cpp(#include "a.h"
|
||||
template <typename T>
|
||||
void fo^o() {})cpp", &EditedFiles);
|
||||
EXPECT_THAT(EditedFiles,
|
||||
testing::ElementsAre(FileWithContents(
|
||||
testPath("a.h"), "template <typename T> void foo(){}")));
|
||||
|
||||
// Specializations needs to be marked "inline".
|
||||
ExtraFiles["a.h"] = R"cpp(
|
||||
template <typename T> void foo();
|
||||
template <> void foo<int>();)cpp";
|
||||
apply(R"cpp(#include "a.h"
|
||||
template <>
|
||||
void fo^o<int>() {})cpp", &EditedFiles);
|
||||
EXPECT_THAT(EditedFiles,
|
||||
testing::ElementsAre(FileWithContents(testPath("a.h"),
|
||||
R"cpp(
|
||||
template <typename T> void foo();
|
||||
template <> inline void foo<int>(){})cpp")));
|
||||
}
|
||||
|
||||
TWEAK_TEST(DefineOutline);
|
||||
TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
|
||||
FileName = "Test.cpp";
|
||||
|
|
Loading…
Reference in New Issue