#include #include #include #include #include "clang/Basic/FileManager.h" #include "CommentSaver.hh" #include "Utilities.hh" CommentSaver::CommentSaver(clang::CompilerInstance & in_ci , HeaderSearchDirs & in_hsd ) : ci(in_ci) , hsd(in_hsd) {} bool CommentSaver::HandleComment(clang::Preprocessor &PP, clang::SourceRange Comment) { //Comment.getBegin().dump(sm) ; //if ( ! sm.isInSystemHeader(Comment.getBegin()) ) { if ( isInUserOrTrickCode( ci , Comment.getBegin() , hsd ) ) { std::string file_name = ci.getSourceManager().getBufferName(Comment.getBegin()) ; unsigned int line_no = ci.getSourceManager().getSpellingLineNumber(Comment.getBegin()) ; comment_map[file_name][line_no] = Comment ; } // returning false means we did not push any text back to the stream for further reads. return false ; } std::string CommentSaver::getComment( clang::SourceRange sr ) { if ( sr.isValid() ) { /* fsl_begin and fsl_end are two pointers into the header file. We want the text between the two pointers. */ clang::FullSourceLoc fsl_begin(sr.getBegin() , ci.getSourceManager()) ; clang::FullSourceLoc fsl_end(sr.getEnd() , ci.getSourceManager()) ; std::string comment_text( fsl_begin.getCharacterData() , (size_t)(fsl_end.getCharacterData() - fsl_begin.getCharacterData())) ; return comment_text ; } /* return an empty string if no comment found on this line of the file. */ return std::string() ; } std::string CommentSaver::getComment( std::string file_name , unsigned int line_no ) { return getComment(comment_map[file_name][line_no]) ; } std::string CommentSaver::getTrickHeaderComment( std::string file_name ) { std::map < unsigned int , clang::SourceRange >::iterator cit ; if ( trick_header_comments.find(file_name) == trick_header_comments.end() ) { trick_header_comments[file_name] = std::string() ; for ( cit = comment_map[file_name].begin() ; cit != comment_map[file_name].end() ; cit++ ) { std::string comment_str = getComment((*cit).second) ; if ( comment_str.find("@trick_parse") != std::string::npos or comment_str.find("\\trick_parse") != std::string::npos ) { trick_header_comments[file_name] = getComment((*cit).second) ; break ; } else { std::transform(comment_str.begin(), comment_str.end(), comment_str.begin(), ::toupper) ; if ( comment_str.find("PURPOSE") != std::string::npos ) { trick_header_comments[file_name] = getComment((*cit).second) ; break ; } } } } return trick_header_comments[file_name] ; } void CommentSaver::getICGField( std::string file_name ) { /* default the icg_no flags to false */ icg_no_found[file_name] = false ; icg_no_comment_found[file_name] = false ; std::string th_str = getTrickHeaderComment(file_name) ; if ( ! th_str.empty() ) { size_t trick_parse_keyword ; if ( (trick_parse_keyword = th_str.find("trick_parse")) != std::string::npos ) { size_t trick_parse_everything = th_str.find("everything", trick_parse_keyword) ; size_t trick_parse_attributes = th_str.find("attributes", trick_parse_keyword) ; size_t trick_parse_dep_only = th_str.find("dependencies_only", trick_parse_keyword) ; size_t closing_brace = th_str.find("}", trick_parse_keyword) ; if ( closing_brace != std::string::npos ) { if ( trick_parse_everything != std::string::npos and trick_parse_everything < closing_brace ) { icg_no_comment_found[file_name] = false ; icg_no_found[file_name] = false ; } else if ( trick_parse_attributes != std::string::npos and trick_parse_attributes < closing_brace ) { icg_no_comment_found[file_name] = true ; icg_no_found[file_name] = false ; } else if ( trick_parse_dep_only != std::string::npos and trick_parse_dep_only < closing_brace ) { icg_no_comment_found[file_name] = false ; icg_no_found[file_name] = true ; } else { std::cout << file_name << ": trick_parse directive found but no argument matched." << std::endl ; std::cout << "Valid arguments are (everything|attributes|dependencies_only)" << std::endl ; exit(-1) ; } } } else { std::transform(th_str.begin(), th_str.end(), th_str.begin(), ::toupper) ; int ret ; regex_t reg_expr ; regmatch_t pmatch[10] ; memset(pmatch , 0 , sizeof(pmatch)) ; /* POSIX regular expressions are always greedy, making our job harder. We have to use several regular expressions to get the types. This was so much easier in perl! */ /* find the ICG field */ ret = regcomp( ®_expr , "(ICG:)" , REG_EXTENDED | REG_ICASE ) ; ret = regexec( ®_expr , th_str.c_str() , 10 , pmatch , 0 ) ; regfree(®_expr) ; if ( ret != 0 ) { return ; } th_str = th_str.substr(pmatch[1].rm_eo) ; /* find the end of the ICG field */ memset(pmatch , 0 , sizeof(pmatch)) ; ret = regcomp( ®_expr , "(\\))" , REG_EXTENDED ) ; ret = regexec( ®_expr , th_str.c_str() , 10 , pmatch , 0 ) ; regfree(®_expr) ; if ( ret != 0 ) { return ; } th_str = th_str.substr(0 , pmatch[1].rm_so) ; /* test for NoComment */ memset(pmatch , 0 , sizeof(pmatch)) ; ret = regcomp( ®_expr , "(NOCOMMENT)$" , REG_EXTENDED ) ; ret = regexec( ®_expr , th_str.c_str() , 10 , pmatch , 0 ) ; regfree(®_expr) ; if ( ret == 0 ) { icg_no_comment_found[file_name] = true ; } /* test for No */ memset(pmatch , 0 , sizeof(pmatch)) ; ret = regcomp( ®_expr , "(NO)$" , REG_EXTENDED ) ; ret = regexec( ®_expr , th_str.c_str() , 10 , pmatch , 0 ) ; regfree(®_expr) ; if ( ret == 0 ) { icg_no_found[file_name] = true ; icg_no_comment_found[file_name] = true ; } } } } bool CommentSaver::hasTrickHeader( std::string file_name ) { std::string th_str = getTrickHeaderComment(file_name) ; return (! th_str.empty()) ; } bool CommentSaver::hasICGNo( std::string file_name ) { if ( icg_no_found.find(file_name) == icg_no_found.end() ) { getICGField(file_name) ; } return icg_no_found[file_name] ; } bool CommentSaver::hasICGNoComment( std::string file_name ) { if ( icg_no_comment_found.find(file_name) == icg_no_comment_found.end() ) { getICGField(file_name) ; } return icg_no_comment_found[file_name] ; } std::set< std::string > CommentSaver::getIgnoreTypes( std::string file_name ) { std::set< std::string > ignore_types ; std::string th_str = getTrickHeaderComment(file_name) ; if ( ! th_str.empty() ) { int ret ; regex_t reg_expr ; regmatch_t pmatch[10] ; //std::cout << "here in getIgnoreTypes\n" << th_str << std::endl ; size_t start = 0 ; start = th_str.find( "trick_exclude_typename") ; while ( start != std::string::npos ) { start += strlen("trick_exclude_typename") ; std::string temp_str = th_str.substr(start) ; memset(pmatch , 0 , sizeof(pmatch)) ; ret = regcomp( ®_expr , "^\\s*\\{\\s*(\\S+)\\s*\\}" , REG_EXTENDED ) ; ret = regexec( ®_expr , temp_str.c_str() , 10 , pmatch , 0 ) ; regfree(®_expr) ; if ( ret == 0 ) { std::string item = temp_str.substr(pmatch[1].rm_so, pmatch[1].rm_eo) ; // regular expression leaving trailing space and brace. Why? item.erase(item.find_first_of(" \t}")) ; //std::cout << "icg_ignore_types " << item << std::endl ; ignore_types.insert(item) ; } start = th_str.find( "trick_exclude_typename", start ) ; } std::transform(th_str.begin(), th_str.end(), th_str.begin(), ::toupper) ; memset(pmatch , 0 , sizeof(pmatch)) ; /* POSIX regular expressions are always greedy, making our job harder. We have to use several regular expressions to get the types. This was so much easier in perl! */ /* find the start of the ICG_IGNORE_TYPES field */ ret = regcomp( ®_expr , "(ICG[ _]IGNORE[ _]TYPE(S)?:)" , REG_EXTENDED | REG_ICASE ) ; ret = regexec( ®_expr , th_str.c_str() , 10 , pmatch , 0 ) ; regfree(®_expr) ; if ( ret != 0 ) { return std::set< std::string >() ; } th_str = th_str.substr(pmatch[1].rm_eo) ; /* find the end of the ICG_IGNORE_TYPES field */ memset(pmatch , 0 , sizeof(pmatch)) ; ret = regcomp( ®_expr , "(\\)\\s*\\))" , REG_EXTENDED ) ; ret = regexec( ®_expr , th_str.c_str() , 10 , pmatch , 0 ) ; regfree(®_expr) ; if ( ret != 0 ) { return std::set< std::string >() ; } th_str = th_str.substr(0 , pmatch[1].rm_so) ; std::replace( th_str.begin(), th_str.end(), '(', ' '); std::replace( th_str.begin(), th_str.end(), ')', ' '); std::replace( th_str.begin(), th_str.end(), '\n', ' '); std::stringstream ss(th_str); std::string item; while (std::getline(ss, item, ' ')) { if ( ! item.empty() ) { //std::cout << "icg_ignore_types " << item << std::endl ; ignore_types.insert(item) ; } } } return ignore_types ; }