Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
DictSelectionReader.cxx
Go to the documentation of this file.
2
3#include "clang/AST/AST.h"
4
5#include "cling/Interpreter/Interpreter.h"
6
8#include "SelectionRules.h"
9#include "TClingUtils.h"
10#include "TClassEdit.h"
11
12#include "RootMetaSelection.h"
13
14#include <iostream>
15#include <sstream>
16
17namespace ROOT {
18namespace Internal {
19
20////////////////////////////////////////////////////////////////////////////////
21
22DictSelectionReader::DictSelectionReader(cling::Interpreter &interp, SelectionRules &selectionRules,
23 const clang::ASTContext &C, ROOT::TMetaUtils::TNormalizedCtxt &normCtxt)
24 : fSelectionRules(selectionRules), fIsFirstPass(true), fNormCtxt(normCtxt)
25{
26 clang::TranslationUnitDecl *translUnitDecl = C.getTranslationUnitDecl();
27
28 {
29 // We push a new transaction because we could deserialize decls here
30 cling::Interpreter::PushTransactionRAII RAII(&interp);
31 // Inspect the AST
32 TraverseDecl(translUnitDecl);
33 }
34
35 // Now re-inspect the AST to find autoselected classes (double-tap)
36 fIsFirstPass = false;
37 if (!fTemplateInfoMap.empty() ||
40 TraverseDecl(translUnitDecl);
41
42 // Now push all the selection rules
43 for (llvm::StringMap<ClassSelectionRule>::iterator it =
46 ++it) {
48 }
49}
50
51////////////////////////////////////////////////////////////////////////////////
52/// If it's not contained by 2 namespaces, drop it.
53
54/**
55 * Check that the recordDecl is enclosed in the ROOT::Meta::Selection namespace,
56 * excluding the portion dedicated the definition of the syntax, which is part
57 * of ROOT, not of the user code.
58 * If performance is needed, an alternative approach to string comparisons
59 * could be adopted. One could use for example hashes of strings in first
60 * approximation.
61 **/
62bool
63DictSelectionReader::InSelectionNamespace(const clang::RecordDecl &recordDecl,
64 const std::string &className)
65{
66 std::list<std::pair<std::string, bool> > enclosingNamespaces;
68 enclosingNamespaces);
69
70 const unsigned int nNs = enclosingNamespaces.size();
71 if (nNs < 3) return false;
72
73 if (enclosingNamespaces.back().second || // is inline namespace
74 enclosingNamespaces.back().first != "ROOT")
75 return false;
76
77 enclosingNamespaces.pop_back();
78 if (enclosingNamespaces.back().second || // is inline namespace
79 enclosingNamespaces.back().first != "Meta")
80 return false;
81
82 enclosingNamespaces.pop_back();
83 if (enclosingNamespaces.back().second || // is inline namespace
84 enclosingNamespaces.back().first != "Selection")
85 return false;
86
87 // Exclude the special names identifying the entities of the selection syntax
88 if (className != "" &&
89 (className.find("MemberAttributes") == 0 ||
90 className.find("ClassAttributes") == 0 || className.find("Keep") == 0))
91 return false;
92
93 return true;
94}
95
96////////////////////////////////////////////////////////////////////////////////
97
98/**
99 * Get the pointer to the template arguments list. Return zero if not available.
100 **/
101const clang::TemplateArgumentList *
102DictSelectionReader::GetTmplArgList(const clang::CXXRecordDecl &cxxRcrdDecl)
103{
104 const clang::ClassTemplateSpecializationDecl *tmplSpecDecl =
105 llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&cxxRcrdDecl);
106
107 if (!tmplSpecDecl) return nullptr;
108
109 return &tmplSpecDecl->getTemplateArgs();
110}
111
112////////////////////////////////////////////////////////////////////////////////
113
114/**
115 * Extract the value of the integral template parameter of a CXXRecordDecl when
116 * it has a certain name. If nothing can be extracted, the value of @c zero
117 * is returned.
118 **/
119template <class T>
120unsigned int
122 const std::string &pattern)
123{
124 const clang::RecordDecl *rcrdDecl =
126 const clang::CXXRecordDecl *cxxRcrdDecl =
127 llvm::dyn_cast<clang::CXXRecordDecl>(rcrdDecl);
128
129 if (!cxxRcrdDecl) return 0;
130
131 const clang::TemplateArgumentList *tmplArgs = GetTmplArgList(*cxxRcrdDecl);
132 if (!tmplArgs) return 0;
133
134 if (std::string::npos == cxxRcrdDecl->getNameAsString().find(pattern))
135 return 0;
136
137 return tmplArgs->get(0).getAsIntegral().getLimitedValue();
138}
139
140////////////////////////////////////////////////////////////////////////////////
141/// Iterate on the members to see if
142/// 1) They are transient
143/// 2) They imply further selection
144
145/**
146 * Loop over the class filelds and take actions according to their properties
147 * 1. Insert a field selection rule marking a member transient
148 * 2. Store in a map the name of the field the type of which should be
149 * autoselected. The key is the name of the class and the value the name of the
150 * field. This information is used in the second pass.
151 **/
152void DictSelectionReader::ManageFields(const clang::RecordDecl &recordDecl,
153 const std::string &className,
155 bool autoselect)
156{
157 std::string pattern = className.substr(0, className.find_first_of("<"));
158
159 for (auto fieldPtr : recordDecl.fields()) {
160
161 unsigned int attrCode =
162 ExtractTemplateArgValue(*fieldPtr, "MemberAttributes");
163
164 if (attrCode == ROOT::Meta::Selection::kMemberNullProperty) continue;
165
166 const char *fieldName = fieldPtr->getName().data();
167
169 if (!autoselect) {
170 fTemplateInfoMap[pattern].fUnsplittableMembers.insert(fieldName);
171 } else {
175 csr.AddFieldSelectionRule(vsr);
176 }
177 }
178
179 if (attrCode & ROOT::Meta::Selection::kTransient) {
180 if (!autoselect) {
181 fTemplateInfoMap[pattern].fTransientMembers.insert(fieldName);
182 } else {
186 csr.AddFieldSelectionRule(vsr);
187 }
188 }
189
191 fAutoSelectedClassFieldNames[className].insert(fieldName);
192 else if (attrCode & ROOT::Meta::Selection::kNoAutoSelected)
193 fNoAutoSelectedClassFieldNames[className].insert(fieldName);
194
195 } // end loop on fields
196}
197
198////////////////////////////////////////////////////////////////////////////////
199/// Check the traits of the class. Useful information may be there
200/// extract mothers, make a switchcase:
201/// 1) templates args are to be skipped
202/// 2) There are properties. Make a loop. make a switch:
203/// 2a) Is splittable
204
205/**
206 * Manage the loop over the base classes.
207 * Initially, the class attributes are identified and selection rules filled
208 * if:
209 * 1. The class is not splittable
210 * Then we look for the traits pointing to the need of hiding template
211 * arguments. This information is stored in the form of a list of pairs, where
212 * the first argument is the pattern of the template instance to match and
213 * the second one the number of arguments to be skipped. This information is
214 * used during the second pass.
215 **/
216void
217DictSelectionReader::ManageBaseClasses(const clang::CXXRecordDecl &cxxRcrdDecl,
218 const std::string &className,
219 bool &autoselect)
220{
221 std::string baseName;
222 clang::ASTContext &C = cxxRcrdDecl.getASTContext();
223 for (auto & base : cxxRcrdDecl.bases()) {
224
225 if (unsigned int nArgsToKeep = ExtractTemplateArgValue(base, "Keep")) {
226 std::string pattern =
227 className.substr(0, className.find_first_of("<"));
228 // Fill the structure holding the template and the number of args to
229 // skip
230 fTemplateInfoMap[pattern] = TemplateInfo(nArgsToKeep);
231 }
232
233 // at most one string comparison...
234 if (autoselect) {
235 auto qt = base.getType();
237 if (baseName == "ROOT::Meta::Selection::SelectNoInstance") autoselect = false;
238 }
239
240 } // end loop on base classes
241}
242
243////////////////////////////////////////////////////////////////////////////////
244
245/**
246 * Manage the first pass over the AST, inspecting only nodes which are within
247 * the selection namespace. Selection rules are directly filled as well as
248 * data sructures re-used during the second pass.
249 **/
250bool DictSelectionReader::FirstPass(const clang::RecordDecl &recordDecl)
251{
252 std::string className;
254 className, *recordDecl.getTypeForDecl(), recordDecl);
255
256 // Strip ROOT::Meta::Selection
257 className.replace(0, 23, "");
258
259 if (!InSelectionNamespace(recordDecl, className)) return true;
260
261 if (!fSelectedRecordDecls.insert(&recordDecl).second) return true;
262
263 bool autoselect = true;
264 if (auto cxxRcrdDecl = llvm::dyn_cast<clang::CXXRecordDecl>(&recordDecl)) {
265 ManageBaseClasses(*cxxRcrdDecl, className, autoselect);
266 }
267
269 const size_t lWedgePos(className.find_first_of("<"));
270 std::string patternName("");
271 if (lWedgePos != std::string::npos &&
272 llvm::isa<clang::ClassTemplateSpecializationDecl>(recordDecl)) {
273 patternName = PatternifyName(className);
275
276 } else {
278 }
279
280 ManageFields(recordDecl, className, csr, autoselect);
281
282 if (!autoselect) return true;
283
284 // Finally add the selection rule
285 fClassNameSelectionRuleMap[patternName.empty() ? className : patternName] =
286 csr;
287
288 return true;
289}
290
291////////////////////////////////////////////////////////////////////////////////
292
293/**
294 * Second pass through the AST. Two operations are performed:
295 * 1. Selection rules for classes to be autoselected are created. The
296 * algorithm works as follows: the members of the classes matching the name of
297 * the classes which contained autoselected members in the selection namespace
298 * are inspected. If a field with the same name of the one which was
299 * autoselected a selection rule based on its typename is built.
300 * 2. If a class is found which is a @c TemplateSpecialisationDecl its
301 * name is checked to match one of the patterns identified during the first
302 * pass. If a match is found, a property is added to the selection rule with
303 * the number of template arguments to keep in order to percolate this
304 * information down to the @c AnnotatedRecordDecl creation which happens in the
305 * @c RScanner .
306 **/
307bool DictSelectionReader::SecondPass(const clang::RecordDecl &recordDecl)
308{
309 using namespace ROOT::TMetaUtils;
310
311 // No interest if we are in the selection namespace
312 if (InSelectionNamespace(recordDecl)) return true;
313
314 std::string className;
315 GetQualifiedName(className, *recordDecl.getTypeForDecl(), recordDecl);
316
317 // If the class is not among those which have fields the type of which are to
318 // be autoselected or excluded
319 if (0 != fAutoSelectedClassFieldNames.count(className) ||
320 0 != fNoAutoSelectedClassFieldNames.count(className)) {
321 // Iterate on fields. If the name of the field is among the ones the types
322 // of which should be (no)autoselected, add a class selection rule
323 std::string typeName;
324 clang::ASTContext &C = recordDecl.getASTContext();
325 for (clang::RecordDecl::field_iterator filedsIt =
326 recordDecl.field_begin();
327 filedsIt != recordDecl.field_end();
328 ++filedsIt) {
329 const std::string fieldName(filedsIt->getNameAsString());
330 bool excluded = 1 == fNoAutoSelectedClassFieldNames[className].count(fieldName);
331 bool selected = 1 == fAutoSelectedClassFieldNames[className].count(fieldName);
332 if (!selected && !excluded)
333 continue;
335 GetFullyQualifiedTypeName(typeName, filedsIt->getType(), C);
336 GetPointeeType(typeName);
337 aSelCsr.SetAttributeValue(propNames::name, typeName);
339 }
340 }
341
342 // If the class is a template instantiation and its name matches one of the
343 // patterns
344
345 // We don't want anything different from templ specialisations
346 if (auto tmplSpecDecl = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&recordDecl)) {
347 for (auto & patternInfoPair : fTemplateInfoMap) {
348 const std::string &pattern = patternInfoPair.first;
349 const TemplateInfo &tInfo = patternInfoPair.second;
350 // Check if we have to add a selection rule for this class
351 if (className.find(pattern) != 0) continue;
352
353 // Take care of the args to keep
354 auto ctd = tmplSpecDecl->getSpecializedTemplate();
355 if (tInfo.fArgsToKeep != -1 && ctd) {
356 fNormCtxt.AddTemplAndNargsToKeep(ctd->getCanonicalDecl(), tInfo.fArgsToKeep);
357 }
358
359 // Now we take care of the transient and unsplittable members
360 if (tInfo.fTransientMembers.empty() && tInfo.fUnsplittableMembers.empty()) continue;
361 clang::ASTContext &C = recordDecl.getASTContext();
362 clang::SourceRange commentRange; // Empty: this is a fake comment
363 std::string userDefinedProperty;
364 userDefinedProperty.reserve(100);
365 for (auto fieldPtr : recordDecl.fields()) {
366 const auto fieldName = fieldPtr->getName().data();
367 if (tInfo.fTransientMembers.count(fieldName) == 1) {
368 userDefinedProperty = "!";
369 } else if (tInfo.fUnsplittableMembers.count(fieldName) == 1) {
370 userDefinedProperty = propNames::comment + propNames::separator + "||";
371 }
372 if (!userDefinedProperty.empty()) {
373 fieldPtr->addAttr(new(C) clang::AnnotateAttr(commentRange, C, userDefinedProperty, 0));
374 userDefinedProperty = "";
375 }
376 }
377 } // End loop on template info
378 }
379
380 return true;
381}
382
383////////////////////////////////////////////////////////////////////////////////
384
385bool DictSelectionReader::VisitRecordDecl(clang::RecordDecl *recordDecl)
386{
387 if (fIsFirstPass)
388 return FirstPass(*recordDecl);
389 else
390 return SecondPass(*recordDecl);
391}
392
393////////////////////////////////////////////////////////////////////////////////
394
395/**
396 * Transform a name of a class instance into a pattern for selection
397 * e.g. myClass<double, int, ...> in the selection namespace
398 * will translate into a pattern of the type myClass<*>
399 **/
400inline std::string DictSelectionReader::PatternifyName(const std::string &className)
401{
402 return className.substr(0, className.find_first_of("<")) + "<*>";
403
404}
405
406////////////////////////////////////////////////////////////////////////////////
407
408/**
409 * Transform the name of the type eliminating the trailing & and *
410 **/
411inline void DictSelectionReader::GetPointeeType(std::string &typeName)
412{
413 while (typeName[typeName.size() - 1] == '*' ||
414 typeName[typeName.size() - 1] == '&') {
415 typeName = typeName.substr(0, typeName.size() - 1);
416 }
417}
418
419}
420}
Select classes and assign properties using C++ syntax.
void SetAttributeValue(const std::string &attributeName, const std::string &attributeValue)
void AddFieldSelectionRule(const VariableSelectionRule &field)
bool fIsFirstPass
Keep trance of the number of passes through the AST.
llvm::StringMap< ClassSelectionRule > fClassNameSelectionRuleMap
Map of the already built sel rules.
void ManageFields(const clang::RecordDecl &, const std::string &, ClassSelectionRule &, bool)
Take care of the class fields.
ROOT::TMetaUtils::TNormalizedCtxt & fNormCtxt
The reference to the normalized context.
llvm::StringMap< std::set< std::string > > fNoAutoSelectedClassFieldNames
Collect the autoexcluded classes.
llvm::StringMap< std::set< std::string > > fAutoSelectedClassFieldNames
Collect the autoselected classes.
void ManageBaseClasses(const clang::CXXRecordDecl &, const std::string &, bool &)
Take care of the class bases.
bool VisitRecordDecl(clang::RecordDecl *)
Visit the entities that needs to be selected.
const clang::TemplateArgumentList * GetTmplArgList(const clang::CXXRecordDecl &)
Get the template arguments list if any.
std::set< const clang::RecordDecl * > fSelectedRecordDecls
The pointers of the selected RecordDecls.
std::string PatternifyName(const std::string &className)
Transform instance name in pattern for selection.
DictSelectionReader(cling::Interpreter &interp, SelectionRules &, const clang::ASTContext &, ROOT::TMetaUtils::TNormalizedCtxt &)
Take the selection rules as input (for consistency w/ other selector interfaces)
SelectionRules & fSelectionRules
The selection rules to be filled.
void GetPointeeType(std::string &typeName)
Get name of the pointee type.
unsigned int ExtractTemplateArgValue(const T &, const std::string &)
Extract the value of the template parameter.
bool InSelectionNamespace(const clang::RecordDecl &, const std::string &str="")
Check if in the ROOT::Meta::Selection namespace.
std::unordered_map< std::string, TemplateInfo > fTemplateInfoMap
List template name - properties map.
bool FirstPass(const clang::RecordDecl &)
First pass on the AST.
bool SecondPass(const clang::RecordDecl &)
Second pass on the AST, using the information of the first one.
void AddTemplAndNargsToKeep(const clang::ClassTemplateDecl *templ, unsigned int i)
The class representing the collection of selection rules.
void AddClassSelectionRule(const ClassSelectionRule &classSel)
@ kNonSplittable
The class cannot be split.
@ kTransient
The data member is transient.
@ kNoAutoSelected
Exclude the type of the member.
@ kAutoSelected
Select the type of the member.
@ kMemberNullProperty
Indicates absence of properties.
static const std::string name("name")
static const std::string pattern("pattern")
static const std::string comment("comment")
clang::RecordDecl * GetUnderlyingRecordDecl(clang::QualType type)
void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const cling::Interpreter &interpreter)
void GetQualifiedName(std::string &qual_name, const clang::QualType &type, const clang::NamedDecl &forcontext)
Main implementation relying on GetFullyQualifiedTypeName All other GetQualifiedName functions leverag...
void ExtractEnclosingNameSpaces(const clang::Decl &, std::list< std::pair< std::string, bool > > &)
Extract the immediately outer namespace and then launch the recursion.
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
std::unordered_set< std::string > fUnsplittableMembers
std::unordered_set< std::string > fTransientMembers