Logo ROOT   6.08/07
Reference Guide
BaseSelectionRule.cxx
Go to the documentation of this file.
1 // @(#)root/core/utils:$Id: BaseSelectionRule.cxx 41697 2011-11-01 21:03:41Z pcanal $
2 // Author: Velislava Spasova September 2010
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2011, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 //////////////////////////////////////////////////////////////////////////
13 // //
14 // BaseSelectionRule //
15 // //
16 // Base selection class from which all //
17 // selection classes should be derived //
18 // //
19 //////////////////////////////////////////////////////////////////////////
20 
21 #include "BaseSelectionRule.h"
22 
23 #include <iostream>
24 #include <string.h>
25 #include <cctype>
26 
27 #include "clang/Basic/SourceLocation.h"
28 #include "clang/Basic/SourceManager.h"
29 #include "clang/AST/DeclCXX.h"
30 #include "clang/AST/ASTContext.h"
31 #include "clang/AST/DeclTemplate.h"
32 
33 #ifdef _WIN32
34 #include "process.h"
35 #endif
36 #include <sys/stat.h>
37 
38 static const char *R__GetDeclSourceFileName(const clang::Decl* D)
39 {
40  clang::ASTContext& ctx = D->getASTContext();
41  clang::SourceManager& SM = ctx.getSourceManager();
42  clang::SourceLocation SL = D->getLocation();
43  // If the class decl is the result of a macpo expansion, take the location
44  // where the macro is "invoked" i.e. expanded at (ExpansionLoc), not the
45  // spelling location (where the delc's tokens come from).
46  if (SL.isMacroID())
47  SL = SM.getExpansionLoc(SL);
48 
49  if (SL.isValid() && SL.isFileID()) {
50  clang::PresumedLoc PLoc = SM.getPresumedLoc(SL);
51  return PLoc.getFilename();
52  }
53  else {
54  return "invalid";
55  }
56 }
57 
58 static bool R__match_filename(const char *srcname,const char *filename)
59 {
60  if (srcname==0) {
61  return false;
62  }
63  if((strcmp(srcname,filename)==0)) {
64  return true;
65  }
66 
67 #ifdef G__WIN32
68  char i1name[_MAX_PATH];
69  char fullfile[_MAX_PATH];
70  _fullpath( i1name, srcname, _MAX_PATH );
71  _fullpath( fullfile, filename, _MAX_PATH );
72  if((stricmp(i1name, fullfile)==0)) return 1;
73 #else
74  struct stat statBufItem;
75  struct stat statBuf;
76  if ( ( 0 == stat( filename, & statBufItem ) )
77  && ( 0 == stat( srcname, & statBuf ) )
78  && ( statBufItem.st_dev == statBuf.st_dev ) // Files on same device
79  && ( statBufItem.st_ino == statBuf.st_ino ) // Files on same inode (but this is not unique on AFS so we need the next 2 test
80  && ( statBufItem.st_size == statBuf.st_size ) // Files of same size
81  && ( statBufItem.st_mtime == statBuf.st_mtime ) // Files modified at the same time
82  ) {
83  return true;
84  }
85 #endif
86  return false;
87 }
88 
89 BaseSelectionRule::BaseSelectionRule(long index, BaseSelectionRule::ESelect sel, const std::string& attributeName, const std::string& attributeValue, cling::Interpreter &interp, const char* selFileName, long lineno)
90  : fIndex(index),fLineNumber(lineno),fSelFileName(selFileName),fIsSelected(sel),fMatchFound(false),fCXXRecordDecl(0),fRequestedType(0),fInterp(&interp)
91 {
92  fAttributes.insert(AttributesMap_t::value_type(attributeName, attributeValue));
93 }
94 
96 {
97  fIsSelected = sel;
98 }
99 
101 {
102  return fIsSelected;
103 }
104 
105 bool BaseSelectionRule::HasAttributeWithName(const std::string& attributeName) const
106 {
107  AttributesMap_t::const_iterator iter = fAttributes.find(attributeName);
108 
109  if(iter!=fAttributes.end()) return true;
110  else return false;
111 }
112 
113 bool BaseSelectionRule::GetAttributeValue(const std::string& attributeName, std::string& returnValue) const
114 {
115  AttributesMap_t::const_iterator iter = fAttributes.find(attributeName);
116 
117  bool retVal = iter!=fAttributes.end();
118  returnValue = retVal ? iter->second : "";
119  return retVal;
120 }
121 
122 void BaseSelectionRule::SetAttributeValue(const std::string& attributeName, const std::string& attributeValue)
123 {
124 
125  std::string localAttributeValue(attributeValue);
126 
127  int pos = attributeName.find("pattern");
128  int pos_file = attributeName.find("file_pattern");
129 
130  // Strip trailing spaces from the name or pattern
131  if (attributeName == "name" || pos> -1){
132  while(std::isspace(*localAttributeValue.begin())) localAttributeValue.erase(localAttributeValue.begin());
133  while(std::isspace(*localAttributeValue.rbegin()))localAttributeValue.erase(localAttributeValue.length()-1);
134  }
135  fAttributes.insert(AttributesMap_t::value_type(attributeName, localAttributeValue));
136 
137  if (pos > -1) {
138  if (pos_file > -1) // if we have file_pattern
139  ProcessPattern(localAttributeValue, fFileSubPatterns);
140  else ProcessPattern(localAttributeValue, fSubPatterns); // if we have pattern and proto_pattern
141  }
142 
143 
144 
145 }
146 
148 {
149  return fAttributes;
150 }
151 
153 {
154  Print(std::cout);
155 }
156 
157 void BaseSelectionRule::PrintAttributes(std::ostream &out, int level) const
158 {
159  std::string tabs;
160  for (int i = 0; i < level; ++i) {
161  tabs+='\t';
162  }
163 
164  if (!fAttributes.empty()) {
165  std::map<std::string,std::string> orderedAttributes(fAttributes.begin(),fAttributes.end());
166  for (auto&& attr : orderedAttributes) {
167  out<<tabs<<attr.first<<" = "<<attr.second<<std::endl;
168  }
169  }
170  else {
171  out<<tabs<<"No attributes"<<std::endl;
172  }
173 }
174 
176 {
177  PrintAttributes(std::cout, level);
178 }
179 #ifndef G__WIN32
180 #include <unistd.h>
181 #endif
183  const std::string& name,
184  const std::string& prototype,
185  bool isLinkdef) const
186 {
187  /* This method returns whether and how the declaration is matching the rule.
188  * It returns one of:
189  * kNoMatch : the rule does match the declaration
190  * kName : the rule match the declaration by name
191  * kPattern : the rule match the declaration via a pattern
192  * kFile : the declaration's file name is match by the rule (either by name or pattern).
193  * To check whether the rule is accepting or vetoing the declaration see the result of
194  * GetSelected().
195  (
196  * We pass as arguments of the method:
197  * name - the name of the Decl
198  * prototype - the prototype of the Decl (if it is function or method, otherwise "")
199  * file_name - name of the source file
200  * isLinkdef - if the selection rules were generating from a linkdef.h file
201  */
202 
203 
204  const std::string& name_value = fName;
205  const std::string& pattern_value = fPattern;
206 
207  // Check if we have in hands a typedef to a RecordDecl
208  const clang::CXXRecordDecl *D = llvm::dyn_cast<clang::CXXRecordDecl>(decl);
209  bool isTypedefNametoRecordDecl = false;
210 
211  if (!D){
212  //Either it's a CXXRecordDecl ot a TypedefNameDecl
213  const clang::TypedefNameDecl* typedefNameDecl = llvm::dyn_cast<clang::TypedefNameDecl> (decl);
214  isTypedefNametoRecordDecl = typedefNameDecl &&
215  ROOT::TMetaUtils::GetUnderlyingRecordDecl(typedefNameDecl->getUnderlyingType());
216  }
217 
218  if (! isTypedefNametoRecordDecl && fCXXRecordDecl !=0 && fCXXRecordDecl != (void*)-1) {
219  const clang::CXXRecordDecl *target = fCXXRecordDecl;
220  if ( target && D && target == D ) {
221  // fprintf(stderr,"DECL MATCH: %s %s\n",name_value.c_str(),name.c_str());
222  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
223  return kName;
224  }
225  } else if (fHasNameAttribute) {
226  if (name_value == name) {
227  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
228  return kName;
229  } else if ( fCXXRecordDecl != (void*)-1 ) {
230  // Try a real match!
231  const clang::CXXRecordDecl *target
233  true /*diagnose*/, 0);
234 
235  if ( target ) {
236  const_cast<BaseSelectionRule*>(this)->fCXXRecordDecl = target;
237  } else {
238  // If the lookup failed, let's not try it again, so mark the value has invalid.
239  const_cast<BaseSelectionRule*>(this)->fCXXRecordDecl = (clang::CXXRecordDecl*)-1;
240  }
241  if ( target && D && target == D ) {
242  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
243  return kName;
244  }
245  }
246  }
247 
248  // do we have matching against the file_name (or file_pattern) attribute and if yes - select or veto
249  const std::string& file_name_value = fFileName;
250  const std::string& file_pattern_value = fFilePattern;
251 
253  const char *file_name = R__GetDeclSourceFileName(decl);
254  bool hasFileMatch = ((fHasFileNameAttribute &&
255  //FIXME It would be much better to cache the rule stat result and compare to the clang::FileEntry
256  (R__match_filename(file_name_value.c_str(),file_name))) ||
257  (fHasFilePatternAttribute && CheckPattern(file_name, file_pattern_value, fFileSubPatterns, isLinkdef)));
258 
259  if (hasFileMatch) {
260  // Reject utility classes defined in ClassImp
261  // when using a file based rule
262  if (!strncmp(name.c_str(), "R__Init", 7) ||
263  strstr(name.c_str(), "::R__Init")) {
264  return kNoMatch;
265  }
266  if (!name.compare(0, 24, "ROOT::R__dummyintdefault")) {
267  return kNoMatch;
268  }
269  if (!name.compare(0, 27, "ROOT::R__dummyVersionNumber")) {
270  return kNoMatch;
271  }
272  if (!name.compare(0, 22, "ROOT::R__dummyStreamer")) {
273  return kNoMatch;
274  }
275  if (name.find("(anonymous namespace)") != std::string::npos) {
276  // Reject items declared in anonymous namespace
277  return kNoMatch;
278  }
279  if (fHasPatternAttribute) {
280  if (CheckPattern(name, pattern_value, fSubPatterns, isLinkdef)) {
281  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
282  return kPattern;
283  }
284  } else {
285  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
286  return kName;
287  }
288  }
289 
290  // We have file_name or file_pattern attribute but the
291  // passed file_name is different than that in the selection rule then return no match
292  return kNoMatch;
293  }
294 
296  {
297  bool patternMatched = CheckPattern(name, pattern_value, fSubPatterns, isLinkdef);
298  if (!patternMatched && !isLinkdef) {
299  std::string auxName(name);
300  std::string &nameNoSpaces = auxName;
301  nameNoSpaces.erase(std::remove_if(nameNoSpaces.begin(), nameNoSpaces.end(), isspace),
302  nameNoSpaces.end());
303  if (name.size() != nameNoSpaces.size()) {
304  patternMatched = CheckPattern(nameNoSpaces, pattern_value, fSubPatterns, isLinkdef);
305  }
306 
307  // For ROOT-6704: use normalised name for matching if the class is in stl
308  // The reason for this check is that we have rules like std::map<*, int>
309  // We do not know how the internal representation of the innocuous "map"
310  // is. We therefore have to act on a nicer name, obtained with TClassEdit
311  // The check ROOT::TMetaUtils::IsStdDropDefaultClass is there to call
312  // TClassEdit only when necessary as it can be expensive, a performance
313  // optimisation.
314  if (!patternMatched &&
315  D &&
316  //ROOT::TMetaUtils::IsStdDropDefaultClass(*D)) {
317  0 != TClassEdit::IsSTLCont(name)) {
318  TClassEdit::GetNormalizedName(auxName, name.c_str());
319  if (name.size() != auxName.size()) {
320  auxName = TClassEdit::InsertStd(auxName.c_str());
321  patternMatched = CheckPattern(auxName, pattern_value, fSubPatterns, isLinkdef);
322  }
323  }
324 
325  }
326  if (patternMatched) {
327  const_cast<BaseSelectionRule *>(this)->SetMatchFound(true);
328  return kPattern;
329  }
330  }
331 
332 
333  // do we have matching against the proto_name (or proto_pattern) attribute and if yes - select or veto
334  // The following selects functions on whether the requested prototype exactly matches the
335  // prototype issued by SelectionRules::GetFunctionPrototype which relies on
336  // ParmVarDecl::getType()->getAsString()
337  // to get the type names. Currently, this does not print the prototype in the usual
338  // human (written) forms. For example:
339  // For Hash have prototype: '(const class TString &)'
340  // For Hash have prototype: '(const class TString*)'
341  // For Hash have prototype: '(const char*)'
342  // In addition, the const can legally be in various place in the type name and thus
343  // a string based match will be hard to work out (it would need to normalize both
344  // the user input string and the clang provided string).
345  // Using lookup form cling would be probably be a better choice.
346  if (!prototype.empty()) {
347  if (fHasProtoNameAttribute && fProtoName==prototype) {
348  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
349  return kName;
350  }
351  if (fHasProtoPatternAttribute && CheckPattern(prototype, fProtoPattern, fSubPatterns, isLinkdef)) {
352  const_cast<BaseSelectionRule*>(this)->SetMatchFound(true);
353  return kPattern;
354  }
355  }
356 
357  return kNoMatch;
358 }
359 
360 
361 /*
362  * This method processes the pattern - which means that it splits it in a list of fSubPatterns.
363  * The idea is the following - if we have a pattern = "this*pat*rn", it will be split in the
364  * following list of subpatterns: "this", "pat", "rn". If we have "this*pat\*rn", it will be
365  * split in "this", "pat*rn", i.e. the star could be escaped.
366  */
367 
368 void BaseSelectionRule::ProcessPattern(const std::string& pattern, std::list<std::string>& out) const
369 {
370  std::string temp = pattern;
371  std::string split;
372  int pos;
373  bool escape = false;
374 
375  if (pattern.size()==1 && pattern == "*"){
376  out.push_back("");
377  return;
378  }
379 
380  while (!temp.empty()){
381  pos = temp.find("*");
382  if (pos == -1) {
383  if (!escape){ // if we don't find a '*', push_back temp (contains the last sub-pattern)
384  out.push_back(temp);
385  // std::cout<<"1. pushed = "<<temp<<std::endl;
386  }
387  else { // if we don't find a star - add temp to split (in split we keep the previous sub-pattern + the last escaped '*')
388  split += temp;
389  out.push_back(split);
390  // std::cout<<"1. pushed = "<<split<<std::endl;
391  }
392  return;
393  }
394  else if (pos == 0) { // we have '*' at the beginning of the pattern; can't have '\' before the '*'
395  temp = temp.substr(1); // remove the '*'
396  }
397  else if (pos == (int)(temp.length()-1)) { // we have '*' at the end of the pattern
398  if (pos > 0 && temp.at(pos-1) == '\\') { // check if we have '\' before the '*'; if yes, we have to escape it
399  split += temp.substr(0, temp.length()-2); // add evrything from the beginning of temp till the '\' to split (where we keep the last sub-pattern)
400  split += temp.at(pos); // add the '*'
401  out.push_back(split); // push_back() split
402  // std::cout<<"3. pushed = "<<split<<std::endl;
403  temp.clear(); // empty temp (the '*' was at the last position of temp, so we don't have anything else to process)
404  }
405  temp = temp.substr(0, (temp.length()-1));
406  }
407  else { // the '*' is at a random position in the pattern
408  if (pos > 0 && temp.at(pos-1) == '\\') { // check if we have '\' before the '*'; if yes, we have to escape it
409  split += temp.substr(0, pos-1); // remove the '\' and add the star to split
410  split += temp.at(pos);
411  escape = true; // escape = true which means that we will add the next sub-pattern to that one
412 
413  // DEBUG std::cout<<"temp = "<<temp<<std::endl;
414  temp = temp.substr(pos);
415  // DEBUG std::cout<<"temp = "<<temp<<", split = "<<split<<std::endl;
416  }
417  else { // if we don't have '\' before the '*'
418  if (escape) {
419  split += temp.substr(0, pos);
420  }
421  else {
422  split = temp.substr(0, pos);
423  }
424  escape = false;
425  temp = temp.substr(pos);
426  out.push_back(split);
427  // std::cout<<"2. pushed = "<<split<<std::endl;
428  // DEBUG std::cout<<"temp = "<<temp<<std::endl;
429  split = "";
430  }
431  }
432  // DEBUG std::cout<<"temp = "<<temp<<std::endl;
433  }
434 }
435 
436 /*
437  * This method checks if the given test string is matched against the pattern
438  */
439 
440 bool BaseSelectionRule::CheckPattern(const std::string& test, const std::string& pattern, const std::list<std::string>& patterns_list, bool isLinkdef) const
441 {
442  bool begin = pattern.front() == '*';
443  if (pattern.size() == 1 && begin) {
444  // We have the simple pattern '*', it matches everything by definition!
445  return true;
446  }
447 
448  std::list<std::string>::const_iterator it = patterns_list.begin();
449  size_t pos1, pos2, pos3;
450  pos1= pos2= pos3= std::string::npos;
451  bool end = pattern.back() == '*';
452 
453  // we first check if the last sub-pattern is contained in the test string
454  const std::string& last = patterns_list.back();
455  size_t pos_end = test.rfind(last);
456 
457  if (pos_end == std::string::npos) { // the last sub-pattern isn't conatained in the test string
458  return false;
459  }
460  if (!end) { // if the pattern doesn't end with '*', the match has to be complete
461  // i.e. if the last sub-pattern is "sub" the test string should end in "sub" ("1111sub" is OK, "1111sub1" is not OK)
462 
463  int len = last.length(); // length of last sub-pattern
464  if ((pos_end+len) < test.length()) {
465  return false;
466  }
467  }
468 
469  // position of the first sub-pattern
470  pos1 = test.find(*it);
471 
472 
473  if (pos1 == std::string::npos || (!begin && pos1 != 0)) { // if the first sub-pattern isn't found in test or if it is found but the
474  // pattern doesn't start with '*' and the sub-pattern is not at the first position
475  //std::cout<<"\tNo match!"<<std::endl;
476  return false;
477  }
478 
479  if (isLinkdef) { // A* selects all global classes, unions, structs but not the nested, i.e. not A::B
480  // A::* selects the nested classes
481  int len = (*it).length();
482  int pos_colon = test.find("::", pos1+len);
483 
484  if (pos_colon > -1) {
485  return false;
486  }
487 
488  }
489 
490  if (patterns_list.size() > 1) {
491  if (((*it).length())+pos1 > pos_end) {
492  // std::cout<<"\tNo match";
493  return false; // end is contained in begin -> test = "A::B" sub-patterns = "A::", "::" will return false
494  }
495  }
496 
497 
498  ++it;
499 
500  for (; it != patterns_list.end(); ++it) {
501  // std::cout<<"sub-pattern = "<<*it<<std::endl;
502  pos2 = test.find(*it);
503  if (pos2 <= pos1) {
504  return false;
505  }
506  pos1 = pos2;
507  }
508 
509  return true;
510 }
511 
512 
514 {
515  fMatchFound = match;
516 }
517 
519 {
520  return fMatchFound;
521 }
522 
524 {
525  return fRequestedType;
526 }
527 
528 void BaseSelectionRule::SetCXXRecordDecl(const clang::CXXRecordDecl *decl, const clang::Type *typeptr)
529 {
530  fCXXRecordDecl = decl;
531  fRequestedType = typeptr;
532 }
533 
535 {
536  std::string value;
543  fHasFromTypedefAttribute = GetAttributeValue("fromTypedef",value);
544  fIsFromTypedef = (value == "true");
545 
547 
548 
550  if (fSubPatterns.empty()) {
551  std::cout<<"Error - A pattern selection without sub patterns." <<std::endl;
552  }
553  }
554 
555 }
556 
557 
static const std::string nArgsToKeep("nArgsToKeep")
ROOT::ESTLType IsSTLCont(std::string_view type)
type : type name: vector<list<classA,allocator>,allocator> result: 0 : not stl container code of cont...
void ProcessPattern(const std::string &pattern, std::list< std::string > &out) const
EMatchType Match(const clang::NamedDecl *decl, const std::string &name, const std::string &prototype, bool isLinkdef) const
virtual void DebugPrint() const
std::string fNArgsToKeep
ESelect GetSelected() const
bool GetMatchFound() const
clang::RecordDecl * GetUnderlyingRecordDecl(clang::QualType type)
std::list< std::string > fSubPatterns
Definition: test.py:1
std::list< std::string > fFileSubPatterns
std::string InsertStd(const char *tname)
const clang::Type * fRequestedType
std::string fFilePattern
const clang::Type * GetRequestedType() const
static const char * R__GetDeclSourceFileName(const clang::Decl *D)
const AttributesMap_t & GetAttributes() const
void PrintAttributes(int level) const
virtual void Print(std::ostream &out) const =0
static const std::string pattern("pattern")
AttributesMap_t fAttributes
bool CheckPattern(const std::string &test, const std::string &pattern, const std::list< std::string > &patterns_list, bool isLinkdef) const
std::string fProtoPattern
void GetNormalizedName(std::string &norm_name, std::string_view name)
Return the normalized name.
Definition: TClassEdit.cxx:788
const clang::CXXRecordDecl * ScopeSearch(const char *name, const cling::Interpreter &gInterp, bool diagnose, const clang::Type **resultType)
Return the scope corresponding to &#39;name&#39; or std::&#39;name&#39;.
Definition: TMetaUtils.cxx:700
static bool R__match_filename(const char *srcname, const char *filename)
std::unordered_map< std::string, std::string > AttributesMap_t
bool GetAttributeValue(const std::string &attributeName, std::string &returnValue) const
Type
enumeration specifying the integration types.
cling::Interpreter * fInterp
void SetSelected(ESelect sel)
BaseSelectionRule(ESelect sel)
void SetAttributeValue(const std::string &attributeName, const std::string &attributeValue)
std::string fProtoName
void SetCXXRecordDecl(const clang::CXXRecordDecl *decl, const clang::Type *typeptr)
void SetMatchFound(bool match)
#define nullptr
Definition: Rtypes.h:87
const clang::CXXRecordDecl * fCXXRecordDecl
bool HasAttributeWithName(const std::string &attributeName) const
char name[80]
Definition: TGX11.cxx:109