forked from andreasfertig/cppinsights
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TemplateHandler.cpp
150 lines (122 loc) · 6.21 KB
/
TemplateHandler.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/******************************************************************************
*
* C++ Insights, copyright (C) by Andreas Fertig
* Distributed under an MIT license. See LICENSE for details
*
****************************************************************************/
#include "TemplateHandler.h"
#include <type_traits>
#include "ClangCompat.h"
#include "CodeGenerator.h"
#include "InsightsHelpers.h"
#include "InsightsMatchers.h"
#include "OutputFormatHelper.h"
#include "llvm/Support/Path.h"
//-----------------------------------------------------------------------------
using namespace clang;
using namespace clang::ast_matchers;
//-----------------------------------------------------------------------------
namespace clang::ast_matchers {
const internal::VariadicDynCastAllOfMatcher<Decl, VarTemplateDecl> varTemplateDecl; // NOLINT
}
namespace clang::insights {
/// \brief Inserts the instantiation point of a template.
//
// This reveals at which place the template is first used.
static void
InsertInstantiationPoint(OutputFormatHelper& outputFormatHelper, const SourceManager& sm, const SourceLocation& instLoc)
{
const auto lineNo = sm.getSpellingLineNumber(instLoc);
const auto& fileId = sm.getFileID(instLoc);
const auto* file = sm.getFileEntryForID(fileId);
if(file) {
const auto fileWithDirName = file->getName();
const auto fileName = llvm::sys::path::filename(fileWithDirName);
outputFormatHelper.AppendNewLine("/* First instantiated from: ", fileName, ":", lineNo, " */");
}
}
//-----------------------------------------------------------------------------
// Workaround to keep clang 6 Linux build alive
template<class T, class U>
inline constexpr bool is_same_v = std::is_same<T, U>::value; // NOLINT
//-----------------------------------------------------------------------------
/// \brief Insert the instantiated template with the resulting code.
template<typename T>
static OutputFormatHelper InsertInstantiatedTemplate(const T& decl, const MatchFinder::MatchResult& result)
{
OutputFormatHelper outputFormatHelper{};
outputFormatHelper.AppendNewLine();
outputFormatHelper.AppendNewLine();
const auto& sm = GetSM(result);
if constexpr(not is_same_v<VarTemplateDecl, T>) { // NOLINT
InsertInstantiationPoint(outputFormatHelper, sm, decl.getPointOfInstantiation());
}
outputFormatHelper.AppendNewLine("#ifdef INSIGHTS_USE_TEMPLATE");
CodeGenerator codeGenerator{outputFormatHelper};
if constexpr(is_same_v<VarTemplateDecl, T>) {
for(const auto& spec : decl.specializations()) {
InsertInstantiationPoint(outputFormatHelper, sm, spec->getPointOfInstantiation());
codeGenerator.InsertArg(spec);
}
} else {
codeGenerator.InsertArg(&decl);
}
outputFormatHelper.AppendNewLine("#endif");
return outputFormatHelper;
}
//-----------------------------------------------------------------------------
TemplateHandler::TemplateHandler(Rewriter& rewrite, MatchFinder& matcher)
: InsightsBase(rewrite)
{
matcher.addMatcher(
functionDecl(allOf(unless(isExpansionInSystemHeader()),
unless(isMacroOrInvalidLocation()),
hasParent(functionTemplateDecl(unless(hasParent(classTemplateSpecializationDecl())),
unless(hasParent(cxxRecordDecl(isLambda()))))),
isTemplateInstantiationPlain()))
.bind("func"),
this);
// match typical use where a class template is defined and it is used later.
matcher.addMatcher(classTemplateSpecializationDecl(unless(isExpansionInSystemHeader()),
hasParent(classTemplateDecl().bind("decl")))
.bind("class"),
this);
// special case, where a class template is defined and somewhere else we request an explicit instantiation
matcher.addMatcher(classTemplateSpecializationDecl(unless(anyOf(isExpansionInSystemHeader(),
hasParent(classTemplateDecl()),
isExplicitTemplateSpecialization())))
.bind("class"),
this);
matcher.addMatcher(
varTemplateDecl(unless(isExpansionInSystemHeader()), unless(hasParent(classTemplateDecl()))).bind("vd"), this);
}
//-----------------------------------------------------------------------------
void TemplateHandler::run(const MatchFinder::MatchResult& result)
{
if(const auto* functionDecl = result.Nodes.getNodeAs<FunctionDecl>("func")) {
if(not functionDecl->getBody()) {
return;
}
OutputFormatHelper outputFormatHelper = InsertInstantiatedTemplate(*functionDecl, result);
const auto endOfCond = FindLocationAfterSemi(GetEndLoc(*functionDecl), result);
InsertIndentedText(endOfCond.getLocWithOffset(1), outputFormatHelper);
} else if(const auto* clsTmplSpecDecl = result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("class")) {
// skip classes/struct's without a definition
if(not clsTmplSpecDecl->hasDefinition()) {
return;
}
OutputFormatHelper outputFormatHelper = InsertInstantiatedTemplate(*clsTmplSpecDecl, result);
if(const auto* clsTmplDecl = result.Nodes.getNodeAs<ClassTemplateDecl>("decl")) {
const auto endOfCond = FindLocationAfterSemi(GetEndLoc(clsTmplDecl), result);
InsertIndentedText(endOfCond, outputFormatHelper);
} else { // explicit specialization, we have to remove the specialization
mRewrite.ReplaceText(clsTmplSpecDecl->getSourceRange(), outputFormatHelper.GetString());
}
} else if(const auto* vd = result.Nodes.getNodeAs<VarTemplateDecl>("vd")) {
OutputFormatHelper outputFormatHelper = InsertInstantiatedTemplate(*vd, result);
const auto endOfCond = FindLocationAfterSemi(GetEndLoc(vd), result);
InsertIndentedText(endOfCond, outputFormatHelper);
}
}
//-----------------------------------------------------------------------------
} // namespace clang::insights