Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
DictSelectionReader.cxx
Go to the documentation of this file.
1 #include "DictSelectionReader.h"
2 
3 #include "clang/AST/AST.h"
4 
5 #include "cling/Interpreter/Interpreter.h"
6 
7 #include "ClassSelectionRule.h"
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 
17 namespace ROOT {
18 namespace Internal {
19 
20 ////////////////////////////////////////////////////////////////////////////////
21 
22 DictSelectionReader::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() ||
38  !fAutoSelectedClassFieldNames.empty() ||
39  !fNoAutoSelectedClassFieldNames.empty())
40  TraverseDecl(translUnitDecl);
41 
42  // Now push all the selection rules
43  for (llvm::StringMap<ClassSelectionRule>::iterator it =
44  fClassNameSelectionRuleMap.begin();
45  it != fClassNameSelectionRuleMap.end();
46  ++it) {
47  fSelectionRules.AddClassSelectionRule(it->second);
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  **/
62 bool
63 DictSelectionReader::InSelectionNamespace(const clang::RecordDecl &recordDecl,
64  const std::string &className)
65 {
66  std::list<std::pair<std::string, bool> > enclosingNamespaces;
67  ROOT::TMetaUtils::ExtractEnclosingNameSpaces(recordDecl,
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  **/
101 const clang::TemplateArgumentList *
102 DictSelectionReader::GetTmplArgList(const clang::CXXRecordDecl &cxxRcrdDecl)
103 {
104  const clang::ClassTemplateSpecializationDecl *tmplSpecDecl =
105  llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(&cxxRcrdDecl);
106 
107  if (!tmplSpecDecl) return 0;
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  **/
119 template <class T>
120 unsigned int
121 DictSelectionReader::ExtractTemplateArgValue(const T &myClass,
122  const std::string &pattern)
123 {
124  const clang::RecordDecl *rcrdDecl =
125  ROOT::TMetaUtils::GetUnderlyingRecordDecl(myClass.getType());
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  **/
152 void DictSelectionReader::ManageFields(const clang::RecordDecl &recordDecl,
153  const std::string &className,
154  ClassSelectionRule &csr,
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 
168  if (attrCode & ROOT::Meta::Selection::kNonSplittable) {
169  if (!autoselect) {
170  fTemplateInfoMap[pattern].fUnsplittableMembers.insert(fieldName);
171  } else {
172  VariableSelectionRule vsr(BaseSelectionRule::kYes);
173  vsr.SetAttributeValue(ROOT::TMetaUtils::propNames::name, fieldName);
174  vsr.SetAttributeValue(ROOT::TMetaUtils::propNames::comment, "||");
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 {
183  VariableSelectionRule vsr(BaseSelectionRule::kYes);
184  vsr.SetAttributeValue(ROOT::TMetaUtils::propNames::name, fieldName);
185  vsr.SetAttributeValue(ROOT::TMetaUtils::propNames::comment, "!");
186  csr.AddFieldSelectionRule(vsr);
187  }
188  }
189 
190  if (attrCode & ROOT::Meta::Selection::kAutoSelected)
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  **/
216 void
217 DictSelectionReader::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();
236  ROOT::TMetaUtils::GetFullyQualifiedTypeName(baseName, qt, C);
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  **/
250 bool DictSelectionReader::FirstPass(const clang::RecordDecl &recordDecl)
251 {
252  std::string className;
253  ROOT::TMetaUtils::GetQualifiedName(
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 
268  ClassSelectionRule csr(BaseSelectionRule::kYes);
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);
274  csr.SetAttributeValue(ROOT::TMetaUtils::propNames::pattern, patternName);
275 
276  } else {
277  csr.SetAttributeValue(ROOT::TMetaUtils::propNames::name, className);
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  **/
307 bool 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;
334  ClassSelectionRule aSelCsr(excluded ? BaseSelectionRule::kNo : BaseSelectionRule::kYes);
335  GetFullyQualifiedTypeName(typeName, filedsIt->getType(), C);
336  GetPointeeType(typeName);
337  aSelCsr.SetAttributeValue(propNames::name, typeName);
338  fSelectionRules.AddClassSelectionRule(aSelCsr);
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 
385 bool 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  **/
400 inline 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  **/
411 inline 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 }