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