2009-01-24 20:28:30 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2016-01-01 14:34:45 +01:00
* Copyright ( C ) 2007 - 2016 Cppcheck team .
2009-01-24 20:28:30 +01:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2009-09-27 17:08:31 +02:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2009-01-24 20:28:30 +01:00
*/
# include "preprocessor.h"
2010-07-21 16:00:12 +02:00
# include "path.h"
2010-07-24 22:12:56 +02:00
# include "errorlogger.h"
2010-07-26 22:05:17 +02:00
# include "settings.h"
2016-07-20 12:21:00 +02:00
# include "simplecpp.h"
2009-01-24 20:28:30 +01:00
# include <algorithm>
# include <sstream>
# include <fstream>
# include <cstdlib>
# include <cctype>
2009-04-05 20:14:02 +02:00
# include <vector>
2009-07-22 18:47:50 +02:00
# include <set>
2009-12-09 17:13:48 +01:00
# include <stack>
2009-01-24 20:28:30 +01:00
2015-12-07 19:54:41 +01:00
/**
* Remove heading and trailing whitespaces from the input parameter .
* If string is all spaces / tabs , return empty string .
* @ param s The string to trim .
*/
static std : : string trim ( const std : : string & s )
{
const std : : string : : size_type beg = s . find_first_not_of ( " \t " ) ;
if ( beg = = std : : string : : npos )
return " " ;
const std : : string : : size_type end = s . find_last_not_of ( " \t " ) ;
return s . substr ( beg , end - beg + 1 ) ;
}
Directive : : Directive ( const std : : string & _file , const int _linenr , const std : : string & _str ) :
file ( _file ) ,
linenr ( _linenr ) ,
str ( _str )
{
str = trim ( str ) ;
}
2011-05-02 14:58:16 +02:00
bool Preprocessor : : missingIncludeFlag ;
2014-05-03 19:31:15 +02:00
bool Preprocessor : : missingSystemIncludeFlag ;
2011-05-02 14:58:16 +02:00
2012-03-27 19:35:41 +02:00
char Preprocessor : : macroChar = char ( 1 ) ;
2015-07-24 13:30:41 +02:00
Preprocessor : : Preprocessor ( Settings & settings , ErrorLogger * errorLogger ) : _settings ( settings ) , _errorLogger ( errorLogger )
2009-06-26 13:19:55 +02:00
{
2016-07-20 12:21:00 +02:00
}
2009-06-26 13:19:55 +02:00
2016-07-20 12:21:00 +02:00
Preprocessor : : ~ Preprocessor ( )
{
for ( std : : map < std : : string , simplecpp : : TokenList * > : : iterator it = tokenlists . begin ( ) ; it ! = tokenlists . end ( ) ; + + it )
delete it - > second ;
2009-06-26 13:19:55 +02:00
}
2010-08-06 19:38:21 +02:00
void Preprocessor : : writeError ( const std : : string & fileName , const unsigned int linenr , ErrorLogger * errorLogger , const std : : string & errorType , const std : : string & errorText )
2009-05-19 21:19:15 +02:00
{
2010-04-02 07:30:58 +02:00
if ( ! errorLogger )
2009-05-19 21:19:15 +02:00
return ;
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
2015-07-26 12:02:45 +02:00
ErrorLogger : : ErrorMessage : : FileLocation loc ( fileName , linenr ) ;
2009-05-19 21:19:15 +02:00
locationList . push_back ( loc ) ;
2009-12-09 17:13:48 +01:00
errorLogger - > reportErr ( ErrorLogger : : ErrorMessage ( locationList ,
2010-07-14 22:11:32 +02:00
Severity : : error ,
2009-12-09 17:13:48 +01:00
errorText ,
2011-04-14 18:02:01 +02:00
errorType ,
false ) ) ;
2009-05-19 21:19:15 +02:00
}
2009-01-24 20:28:30 +01:00
2016-07-21 07:57:23 +02:00
static void inlineSuppressions ( const simplecpp : : TokenList & tokens , Settings & _settings )
2012-01-02 15:52:19 +01:00
{
2016-07-20 12:21:00 +02:00
std : : list < std : : string > suppressionIDs ;
2012-01-02 15:52:19 +01:00
2016-07-20 12:21:00 +02:00
for ( const simplecpp : : Token * tok = tokens . cbegin ( ) ; tok ; tok = tok - > next ) {
if ( tok - > comment ) {
std : : istringstream iss ( tok - > str . substr ( 2 ) ) ;
std : : string word ;
iss > > word ;
if ( word ! = " cppcheck-suppress " )
continue ;
iss > > word ;
if ( iss )
suppressionIDs . push_back ( word ) ;
continue ;
2010-04-15 18:37:51 +02:00
}
2013-11-15 19:21:21 +01:00
2016-07-20 12:21:00 +02:00
if ( suppressionIDs . empty ( ) )
continue ;
2013-06-19 17:42:55 +02:00
2016-07-20 12:21:00 +02:00
// Relative filename
std : : string relativeFilename ( tok - > location . file ( ) ) ;
if ( _settings . relativePaths ) {
for ( std : : size_t j = 0U ; j < _settings . basePaths . size ( ) ; + + j ) {
const std : : string bp = _settings . basePaths [ j ] + " / " ;
if ( relativeFilename . compare ( 0 , bp . size ( ) , bp ) = = 0 ) {
relativeFilename = relativeFilename . substr ( bp . size ( ) ) ;
2013-06-19 17:42:55 +02:00
}
}
}
2016-07-20 12:21:00 +02:00
// Add the suppressions.
for ( std : : list < std : : string > : : const_iterator it = suppressionIDs . begin ( ) ; it ! = suppressionIDs . end ( ) ; + + it ) {
_settings . nomsg . addSuppression ( * it , relativeFilename , tok - > location . line ) ;
}
suppressionIDs . clear ( ) ;
}
2013-06-19 17:42:55 +02:00
}
2016-07-21 07:57:23 +02:00
void Preprocessor : : inlineSuppressions ( const simplecpp : : TokenList & tokens )
{
if ( ! _settings . inlineSuppressions )
return ;
: : inlineSuppressions ( tokens , _settings ) ;
for ( std : : map < std : : string , simplecpp : : TokenList * > : : const_iterator it = tokenlists . begin ( ) ; it ! = tokenlists . end ( ) ; + + it ) {
if ( it - > second )
: : inlineSuppressions ( * it - > second , _settings ) ;
}
}
2016-07-20 12:21:00 +02:00
static std : : string readcondition ( const simplecpp : : Token * iftok , const std : : set < std : : string > & defined )
2009-01-24 20:28:30 +01:00
{
2016-07-20 12:21:00 +02:00
const simplecpp : : Token * cond = iftok - > next ;
if ( ! cond )
2013-07-24 13:06:59 +02:00
return " " ;
2016-07-20 12:21:00 +02:00
if ( cond - > str = = " 0 " )
return " 0 " ;
2009-05-13 21:18:02 +02:00
2016-07-20 12:21:00 +02:00
if ( cond - > name & & cond - > next & & iftok - > location . sameline ( cond - > location ) & & ! iftok - > location . sameline ( cond - > next - > location ) ) {
if ( defined . find ( cond - > str ) = = defined . end ( ) )
return cond - > str ;
2009-05-13 21:18:02 +02:00
}
2011-02-11 06:30:42 +01:00
2016-07-20 12:21:00 +02:00
if ( cond - > op = = ' ( ' & & cond - > next & & cond - > next - > name & & cond - > next - > next & & cond - > next - > next - > op = = ' ) ' ) {
if ( defined . find ( cond - > next - > str ) = = defined . end ( ) )
return cond - > next - > str ;
}
2011-02-11 06:30:42 +01:00
2016-07-20 12:21:00 +02:00
if ( cond - > name & & cond - > next & & cond - > next - > str = = " == " & & cond - > next - > next & & cond - > next - > next - > number ) {
if ( defined . find ( cond - > str ) = = defined . end ( ) )
return cond - > str + ' = ' + cond - > next - > next - > str ;
}
2011-03-01 07:50:17 +01:00
2016-07-20 12:21:00 +02:00
std : : set < std : : string > configset ;
for ( ; cond & & iftok - > location . sameline ( cond - > location ) ; cond = cond - > next ) {
if ( cond - > op = = ' ! ' )
break ;
if ( cond - > str = = " defined " ) {
if ( ! cond - > next )
break ;
const simplecpp : : Token * dtok = nullptr ;
if ( cond - > next - > op = = ' ( ' )
dtok = cond - > next - > next ;
else if ( cond - > next - > name )
dtok = cond - > next ;
if ( dtok & & dtok - > name & & defined . find ( dtok - > str ) = = defined . end ( ) )
configset . insert ( dtok - > str ) ;
}
}
std : : string cfg ;
for ( std : : set < std : : string > : : const_iterator it = configset . begin ( ) ; it ! = configset . end ( ) ; + + it ) {
if ( ! cfg . empty ( ) )
cfg + = ' ; ' ;
cfg + = * it ;
}
return cfg ;
2011-02-11 06:30:42 +01:00
}
2016-07-20 12:21:00 +02:00
static std : : string cfg ( const std : : vector < std : : string > & configs )
{
std : : set < std : : string > configs2 ( configs . begin ( ) , configs . end ( ) ) ;
std : : string ret ;
for ( std : : set < std : : string > : : const_iterator it = configs2 . begin ( ) ; it ! = configs2 . end ( ) ; + + it ) {
if ( it - > empty ( ) )
continue ;
if ( * it = = " 0 " )
return " " ;
if ( ! ret . empty ( ) )
ret + = ' ; ' ;
ret + = * it ;
}
return ret ;
}
2013-09-01 07:13:48 +02:00
2016-07-20 12:21:00 +02:00
static void getConfigs ( const simplecpp : : TokenList & tokens , std : : set < std : : string > & defined , std : : set < std : : string > & ret )
2013-09-01 07:13:48 +02:00
{
2016-07-20 12:21:00 +02:00
std : : vector < std : : string > configs_if ;
std : : vector < std : : string > configs_ifndef ;
for ( const simplecpp : : Token * tok = tokens . cbegin ( ) ; tok ; tok = tok - > next ) {
if ( tok - > op ! = ' # ' )
2013-09-01 07:13:48 +02:00
continue ;
2016-07-20 12:21:00 +02:00
if ( tok - > previous & & tok - > location . sameline ( tok - > previous - > location ) )
2013-09-01 07:13:48 +02:00
continue ;
2016-07-20 12:21:00 +02:00
if ( ! tok - > next )
continue ;
if ( tok - > next - > str = = " ifdef " | | tok - > next - > str = = " ifndef " | | tok - > next - > str = = " if " ) {
std : : string config ;
if ( tok - > next - > str = = " ifdef " | | tok - > next - > str = = " ifndef " ) {
if ( tok - > next - > next & & tok - > next - > next - > name & & tok - > location . sameline ( tok - > next - > next - > location ) & & tok - > next - > next - > next & & ! tok - > location . sameline ( tok - > next - > next - > next - > location ) )
config = tok - > next - > next - > str ;
if ( defined . find ( config ) ! = defined . end ( ) )
config . clear ( ) ;
} else if ( tok - > next - > str = = " if " ) {
config = readcondition ( tok - > next , defined ) ;
}
configs_if . push_back ( ( tok - > next - > str = = " ifndef " ) ? std : : string ( ) : config ) ;
configs_ifndef . push_back ( ( tok - > next - > str = = " ifndef " ) ? config : std : : string ( ) ) ;
ret . insert ( cfg ( configs_if ) ) ;
} else if ( tok - > next - > str = = " elif " ) {
2016-07-20 20:39:03 +02:00
if ( ! configs_if . empty ( ) )
configs_if . pop_back ( ) ;
2016-07-20 12:21:00 +02:00
configs_if . push_back ( readcondition ( tok - > next , defined ) ) ;
ret . insert ( cfg ( configs_if ) ) ;
} else if ( tok - > next - > str = = " else " ) {
2016-07-20 20:39:03 +02:00
if ( ! configs_if . empty ( ) )
configs_if . pop_back ( ) ;
2016-07-20 12:21:00 +02:00
configs_if . push_back ( configs_ifndef . back ( ) ) ;
ret . insert ( cfg ( configs_if ) ) ;
} else if ( tok - > next - > str = = " endif " & & tok - > location . sameline ( tok - > next - > location ) ) {
if ( ! configs_if . empty ( ) )
configs_if . pop_back ( ) ;
if ( ! configs_ifndef . empty ( ) )
configs_ifndef . pop_back ( ) ;
} else if ( tok - > next - > str = = " define " & & tok - > next - > next & & tok - > next - > next - > name & & tok - > location . sameline ( tok - > next - > next - > location ) ) {
defined . insert ( tok - > next - > next - > str ) ;
}
}
}
std : : set < std : : string > Preprocessor : : getConfigs ( const simplecpp : : TokenList & tokens ) const
{
std : : set < std : : string > ret ;
ret . insert ( " " ) ;
simplecpp : : TokenList tokens2 ( tokens ) ;
tokens2 . removeComments ( ) ;
if ( ! tokens2 . cbegin ( ) )
return ret ;
std : : set < std : : string > defined ;
defined . insert ( " __cplusplus " ) ;
: : getConfigs ( tokens2 , defined , ret ) ;
for ( std : : map < std : : string , simplecpp : : TokenList * > : : const_iterator it = tokenlists . begin ( ) ; it ! = tokenlists . end ( ) ; + + it )
: : getConfigs ( * ( it - > second ) , defined , ret ) ;
2013-09-01 07:13:48 +02:00
2016-07-20 12:21:00 +02:00
return ret ;
2013-09-01 07:13:48 +02:00
}
2012-09-01 10:32:27 +02:00
std : : string Preprocessor : : preprocessCleanupDirectives ( const std : : string & processedFile )
2011-02-11 06:30:42 +01:00
{
std : : ostringstream code ;
std : : istringstream sstr ( processedFile ) ;
std : : string line ;
2011-10-13 20:53:06 +02:00
while ( std : : getline ( sstr , line ) ) {
2011-02-11 06:30:42 +01:00
// Trim lines..
if ( ! line . empty ( ) & & line [ 0 ] = = ' ' )
line . erase ( 0 , line . find_first_not_of ( " " ) ) ;
2015-07-25 17:19:53 +02:00
if ( ! line . empty ( ) & & line . back ( ) = = ' ' )
2011-02-11 06:30:42 +01:00
line . erase ( line . find_last_not_of ( " " ) + 1 ) ;
// Preprocessor
2011-10-13 20:53:06 +02:00
if ( ! line . empty ( ) & & line [ 0 ] = = ' # ' ) {
enum {
2011-02-11 06:30:42 +01:00
ESC_NONE ,
ESC_SINGLE ,
ESC_DOUBLE
} escapeStatus = ESC_NONE ;
char prev = ' ' ; // hack to make it skip spaces between # and the directive
code < < " # " ;
std : : string : : const_iterator i = line . begin ( ) ;
2011-02-13 23:57:07 +01:00
+ + i ;
2011-02-11 06:30:42 +01:00
// need space.. #if( => #if (
bool needSpace = true ;
2011-10-13 20:53:06 +02:00
while ( i ! = line . end ( ) ) {
2011-02-11 06:30:42 +01:00
// disable esc-mode
2011-10-13 20:53:06 +02:00
if ( escapeStatus ! = ESC_NONE ) {
if ( prev ! = ' \\ ' & & escapeStatus = = ESC_SINGLE & & * i = = ' \' ' ) {
2011-02-11 06:30:42 +01:00
escapeStatus = ESC_NONE ;
}
2011-10-13 20:53:06 +02:00
if ( prev ! = ' \\ ' & & escapeStatus = = ESC_DOUBLE & & * i = = ' " ' ) {
2011-02-11 06:30:42 +01:00
escapeStatus = ESC_NONE ;
}
2011-10-13 20:53:06 +02:00
} else {
2011-02-11 06:30:42 +01:00
// enable esc-mode
if ( escapeStatus = = ESC_NONE & & * i = = ' " ' )
escapeStatus = ESC_DOUBLE ;
if ( escapeStatus = = ESC_NONE & & * i = = ' \' ' )
escapeStatus = ESC_SINGLE ;
}
// skip double whitespace between arguments
2011-10-13 20:53:06 +02:00
if ( escapeStatus = = ESC_NONE & & prev = = ' ' & & * i = = ' ' ) {
2011-02-13 23:57:07 +01:00
+ + i ;
2011-02-11 06:30:42 +01:00
continue ;
}
// Convert #if( to "#if ("
2011-10-13 20:53:06 +02:00
if ( escapeStatus = = ESC_NONE ) {
if ( needSpace ) {
2011-02-11 06:30:42 +01:00
if ( * i = = ' ( ' | | * i = = ' ! ' )
code < < " " ;
2014-03-18 21:41:47 +01:00
else if ( ! std : : isalpha ( ( unsigned char ) * i ) )
2011-02-11 06:30:42 +01:00
needSpace = false ;
}
if ( * i = = ' # ' )
needSpace = true ;
}
code < < * i ;
2011-10-13 20:53:06 +02:00
if ( escapeStatus ! = ESC_NONE & & prev = = ' \\ ' & & * i = = ' \\ ' ) {
2011-02-11 06:30:42 +01:00
prev = ' ' ;
2011-10-13 20:53:06 +02:00
} else {
2011-02-11 06:30:42 +01:00
prev = * i ;
}
2011-02-13 23:57:07 +01:00
+ + i ;
2011-02-11 06:30:42 +01:00
}
2011-10-13 20:53:06 +02:00
if ( escapeStatus ! = ESC_NONE ) {
2011-02-11 06:30:42 +01:00
// unmatched quotes.. compiler should probably complain about this..
}
2011-10-13 20:53:06 +02:00
} else {
2011-02-11 06:30:42 +01:00
// Do not mess with regular code..
code < < line ;
}
code < < ( sstr . eof ( ) ? " " : " \n " ) ;
}
return code . str ( ) ;
2009-05-13 21:18:02 +02:00
}
2016-07-20 12:21:00 +02:00
void Preprocessor : : preprocess ( std : : istream & istr , std : : map < std : : string , std : : string > & result , const std : : string & filename , const std : : list < std : : string > & includePaths )
2009-10-04 07:25:30 +02:00
{
2016-07-20 12:21:00 +02:00
( void ) includePaths ;
2009-10-04 07:25:30 +02:00
2016-07-20 12:21:00 +02:00
simplecpp : : OutputList outputList ;
std : : vector < std : : string > files ;
const simplecpp : : TokenList tokens1 ( istr , files , filename , & outputList ) ;
2009-10-04 07:25:30 +02:00
2009-09-11 21:22:41 +02:00
2016-07-20 12:21:00 +02:00
const std : : set < std : : string > configs = getConfigs ( tokens1 ) ;
2009-09-11 21:22:41 +02:00
2016-07-20 12:21:00 +02:00
for ( std : : set < std : : string > : : const_iterator it = configs . begin ( ) ; it ! = configs . end ( ) ; + + it ) {
2015-07-24 13:30:41 +02:00
if ( _settings . userUndefs . find ( * it ) = = _settings . userUndefs . end ( ) ) {
2016-07-20 12:21:00 +02:00
result [ * it ] = getcode ( tokens1 , * it , files , false ) ;
2013-06-08 16:46:54 +02:00
}
2011-11-30 20:24:01 +01:00
}
2009-01-24 20:28:30 +01:00
}
std : : string Preprocessor : : removeSpaceNearNL ( const std : : string & str )
{
std : : string tmp ;
2015-03-12 21:16:54 +01:00
char prev = ' \n ' ; // treat start of file as newline
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 0 ; i < str . size ( ) ; i + + ) {
2010-04-02 07:30:58 +02:00
if ( str [ i ] = = ' ' & &
2015-03-12 21:16:54 +01:00
( prev = = ' \n ' | |
i + 1 > = str . size ( ) | | // treat end of file as newline
str [ i + 1 ] = = ' \n '
2010-04-02 07:30:58 +02:00
)
2011-10-13 20:53:06 +02:00
) {
2009-01-24 20:28:30 +01:00
// Ignore space that has new line in either side of it
2011-10-13 20:53:06 +02:00
} else {
2009-01-24 20:28:30 +01:00
tmp . append ( 1 , str [ i ] ) ;
2010-08-06 19:38:21 +02:00
prev = str [ i ] ;
2009-01-24 20:28:30 +01:00
}
}
return tmp ;
}
2010-04-15 22:45:38 +02:00
void Preprocessor : : preprocessWhitespaces ( std : : string & processedFile )
2009-01-24 20:28:30 +01:00
{
// Replace all tabs with spaces..
std : : replace ( processedFile . begin ( ) , processedFile . end ( ) , ' \t ' , ' ' ) ;
// Remove space characters that are after or before new line character
processedFile = removeSpaceNearNL ( processedFile ) ;
2010-04-15 22:45:38 +02:00
}
void Preprocessor : : preprocess ( std : : istream & srcCodeStream , std : : string & processedFile , std : : list < std : : string > & resultConfigurations , const std : : string & filename , const std : : list < std : : string > & includePaths )
{
2016-07-20 12:21:00 +02:00
( void ) includePaths ;
2012-12-27 16:52:31 +01:00
2010-09-03 13:30:49 +02:00
if ( file0 . empty ( ) )
file0 = filename ;
2016-07-20 12:21:00 +02:00
simplecpp : : OutputList outputList ;
std : : vector < std : : string > files ;
const simplecpp : : TokenList tokens1 ( srcCodeStream , files , filename , & outputList ) ;
2012-12-27 16:52:31 +01:00
2016-07-20 12:21:00 +02:00
const std : : set < std : : string > configs = getConfigs ( tokens1 ) ;
for ( std : : set < std : : string > : : const_iterator it = configs . begin ( ) ; it ! = configs . end ( ) ; + + it )
resultConfigurations . push_back ( * it ) ;
2012-12-27 16:52:31 +01:00
2016-07-20 12:21:00 +02:00
processedFile = tokens1 . stringify ( ) ;
}
2015-07-24 13:30:41 +02:00
2016-07-20 12:21:00 +02:00
static void splitcfg ( const std : : string & cfg , std : : list < std : : string > & defines , const std : : string & defaultValue )
{
for ( std : : string : : size_type pos1 = 0U ; pos1 < cfg . size ( ) ; ) {
const std : : string : : size_type pos2 = cfg . find ( " ; " , pos1 ) ;
std : : string def = ( pos2 = = std : : string : : npos ) ? cfg . substr ( pos1 ) : cfg . substr ( pos1 , pos2 - pos1 ) ;
if ( ! defaultValue . empty ( ) & & def . find ( " = " ) = = std : : string : : npos )
def + = ' = ' + defaultValue ;
defines . push_back ( def ) ;
pos1 = ( pos2 = = std : : string : : npos ) ? pos2 : pos2 + 1U ;
2015-07-24 13:30:41 +02:00
}
2016-07-20 12:21:00 +02:00
}
2015-07-24 13:30:41 +02:00
2016-07-20 12:21:00 +02:00
void Preprocessor : : loadFiles ( const simplecpp : : TokenList & rawtokens , std : : vector < std : : string > & files )
{
const std : : string filename ( files [ 0 ] ) ;
2012-12-27 16:52:31 +01:00
2016-07-20 12:21:00 +02:00
// Create a map for the cfg for faster access to defines
simplecpp : : DUI dui ;
2012-12-27 16:52:31 +01:00
2016-07-20 12:21:00 +02:00
splitcfg ( _settings . userDefines , dui . defines , " 1 " ) ;
for ( std : : vector < std : : string > : : const_iterator it = _settings . library . defines . begin ( ) ; it ! = _settings . library . defines . end ( ) ; + + it ) {
if ( it - > compare ( 0 , 8 , " #define " ) ! = 0 )
continue ;
std : : string s = it - > substr ( 8 ) ;
std : : string : : size_type pos = s . find_first_of ( " ( " ) ;
if ( pos = = std : : string : : npos ) {
dui . defines . push_back ( s ) ;
continue ;
}
if ( s [ pos ] = = ' ' ) {
s [ pos ] = ' = ' ;
} else {
s [ s . find ( " ) " ) + 1 ] = ' = ' ;
2009-07-22 20:11:27 +02:00
}
2016-07-20 12:21:00 +02:00
dui . defines . push_back ( s ) ;
2009-07-22 20:11:27 +02:00
}
2016-07-20 12:21:00 +02:00
if ( Path : : isCPP ( filename ) )
dui . defines . push_back ( " __cplusplus " ) ;
2011-10-24 20:59:46 +02:00
2016-07-20 12:21:00 +02:00
dui . undefined = _settings . userUndefs ;
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
dui . includePaths = _settings . includePaths ;
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
simplecpp : : OutputList outputList ;
2012-07-18 20:57:00 +02:00
2016-07-20 12:21:00 +02:00
tokenlists = simplecpp : : load ( rawtokens , files , dui , & outputList ) ;
2009-01-24 20:28:30 +01:00
}
2016-07-21 07:48:17 +02:00
void Preprocessor : : removeComments ( )
{
for ( std : : map < std : : string , simplecpp : : TokenList * > : : iterator it = tokenlists . begin ( ) ; it ! = tokenlists . end ( ) ; + + it ) {
if ( it - > second )
it - > second - > removeComments ( ) ;
}
}
2016-07-20 12:21:00 +02:00
std : : string Preprocessor : : getcode ( const simplecpp : : TokenList & tokens1 , const std : : string & cfg , std : : vector < std : : string > & files , const bool writeLocations )
2012-07-18 20:57:00 +02:00
{
2016-07-20 12:21:00 +02:00
const std : : string filename ( files [ 0 ] ) ;
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
// Create a map for the cfg for faster access to defines
simplecpp : : DUI dui ;
splitcfg ( _settings . userDefines , dui . defines , " 1 " ) ;
splitcfg ( cfg , dui . defines , " " ) ;
for ( std : : vector < std : : string > : : const_iterator it = _settings . library . defines . begin ( ) ; it ! = _settings . library . defines . end ( ) ; + + it ) {
if ( it - > compare ( 0 , 8 , " #define " ) ! = 0 )
continue ;
std : : string s = it - > substr ( 8 ) ;
std : : string : : size_type pos = s . find_first_of ( " ( " ) ;
if ( pos = = std : : string : : npos ) {
dui . defines . push_back ( s ) ;
continue ;
}
if ( s [ pos ] = = ' ' ) {
s [ pos ] = ' = ' ;
} else {
s [ s . find ( " ) " ) + 1 ] = ' = ' ;
2012-07-18 20:57:00 +02:00
}
2016-07-20 12:21:00 +02:00
dui . defines . push_back ( s ) ;
2012-07-18 20:57:00 +02:00
}
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
if ( Path : : isCPP ( filename ) )
dui . defines . push_back ( " __cplusplus " ) ;
2011-01-31 01:47:49 +01:00
2016-07-20 12:21:00 +02:00
dui . undefined = _settings . userUndefs ;
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
dui . includePaths = _settings . includePaths ;
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
simplecpp : : OutputList outputList ;
std : : list < simplecpp : : MacroUsage > macroUsage ;
const simplecpp : : TokenList & tokens2 = simplecpp : : preprocess ( tokens1 , files , tokenlists , dui , & outputList , & macroUsage ) ;
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
bool showerror = ( ! _settings . userDefines . empty ( ) & & ! _settings . force ) ;
for ( simplecpp : : OutputList : : const_iterator it = outputList . begin ( ) ; it ! = outputList . end ( ) ; + + it ) {
2016-07-21 12:47:00 +02:00
switch ( it - > type ) {
case simplecpp : : Output : : ERROR :
2016-07-20 12:21:00 +02:00
if ( it - > msg . compare ( 0 , 6 , " #error " ) ! = 0 | | showerror )
error ( it - > location . file ( ) , it - > location . line , it - > msg ) ;
return " " ;
2016-07-21 12:47:00 +02:00
case simplecpp : : Output : : WARNING :
break ;
case simplecpp : : Output : : MISSING_INCLUDE : {
const std : : string : : size_type pos1 = it - > msg . find_first_of ( " < \" " ) ;
const std : : string : : size_type pos2 = it - > msg . find_first_of ( " > \" " , pos1 + 1U ) ;
if ( pos1 < pos2 & & pos2 ! = std : : string : : npos )
missingInclude ( it - > location . file ( ) , it - > location . line , it - > msg . substr ( pos1 + 1 , pos2 - pos1 - 1 ) , it - > msg [ pos1 ] = = ' \" ' ? UserHeader : SystemHeader ) ;
2009-12-13 15:23:44 +01:00
}
2016-07-21 12:47:00 +02:00
break ;
} ;
2009-06-26 13:19:55 +02:00
}
2016-07-20 12:21:00 +02:00
// directive list..
directives . clear ( ) ;
for ( const simplecpp : : Token * tok = tokens1 . cbegin ( ) ; tok ; tok = tok ? tok - > next : nullptr ) {
if ( ( tok - > op ! = ' # ' ) | | ( tok - > previous & & tok - > previous - > location . line = = tok - > location . line ) )
2009-12-11 19:28:37 +01:00
continue ;
2016-07-20 12:21:00 +02:00
if ( tok - > next & & tok - > next - > str = = " endfile " )
2009-12-11 19:28:37 +01:00
continue ;
2016-07-20 12:21:00 +02:00
Directive directive ( tok - > location . file ( ) , tok - > location . line , " " ) ;
for ( const simplecpp : : Token * tok2 = tok ; tok2 & & tok2 - > location . line = = directive . linenr ; tok2 = tok2 - > next ) {
if ( tok2 - > comment )
2009-12-13 17:18:27 +01:00
continue ;
2016-07-20 12:21:00 +02:00
if ( ! directive . str . empty ( ) & & ( tok2 - > location . col > tok2 - > previous - > location . col + tok2 - > previous - > str . size ( ) ) )
directive . str + = ' ' ;
if ( directive . str = = " # " & & tok2 - > str = = " file " )
directive . str + = " include " ;
2010-08-26 21:33:45 +02:00
else
2016-07-20 12:21:00 +02:00
directive . str + = tok2 - > str ;
2011-07-16 09:24:27 +02:00
}
2016-07-20 12:21:00 +02:00
directives . push_back ( directive ) ;
2011-07-16 09:24:27 +02:00
}
2016-07-20 12:21:00 +02:00
// ensure that guessed define macros without value are not used in the code
for ( std : : list < std : : string > : : const_iterator defineIt = dui . defines . begin ( ) ; defineIt ! = dui . defines . end ( ) ; + + defineIt ) {
if ( defineIt - > find ( " = " ) ! = std : : string : : npos )
2015-12-07 19:54:41 +01:00
continue ;
2016-07-20 12:21:00 +02:00
const std : : string macroName = defineIt - > substr ( 0 , std : : min ( defineIt - > find ( " = " ) , defineIt - > find ( " ( " ) ) ) ;
for ( std : : list < simplecpp : : MacroUsage > : : const_iterator usageIt = macroUsage . begin ( ) ; usageIt ! = macroUsage . end ( ) ; + + usageIt ) {
const simplecpp : : MacroUsage & mu = * usageIt ;
if ( mu . macroName ! = macroName )
continue ;
bool directiveLocation = false ;
for ( std : : list < Directive > : : const_iterator dirIt = directives . begin ( ) ; dirIt ! = directives . end ( ) ; + + dirIt ) {
if ( mu . useLocation . file ( ) = = dirIt - > file & & mu . useLocation . line = = dirIt - > linenr ) {
directiveLocation = true ;
2014-06-09 11:35:30 +02:00
break ;
}
}
2016-07-20 12:21:00 +02:00
if ( ! directiveLocation ) {
if ( _settings . isEnabled ( " information " ) )
validateCfgError ( cfg , macroName ) ;
return " " ;
2011-10-24 08:11:44 +02:00
}
2016-07-20 12:21:00 +02:00
}
}
2012-01-21 12:51:54 +01:00
2016-07-20 12:21:00 +02:00
// assembler code locations..
std : : set < simplecpp : : Location > assemblerLocations ;
for ( std : : list < Directive > : : const_iterator dirIt = directives . begin ( ) ; dirIt ! = directives . end ( ) ; + + dirIt ) {
const Directive & d1 = * dirIt ;
if ( d1 . str . compare ( 0 , 11 , " #pragma asm " ) ! = 0 )
continue ;
std : : list < Directive > : : const_iterator dirIt2 = dirIt ;
+ + dirIt2 ;
if ( dirIt2 = = directives . end ( ) )
continue ;
2011-12-09 21:57:06 +01:00
2016-07-20 12:21:00 +02:00
const Directive & d2 = * dirIt2 ;
if ( d2 . str . compare ( 0 , 14 , " #pragma endasm " ) ! = 0 | | d1 . file ! = d2 . file )
continue ;
2011-10-17 20:12:46 +02:00
2016-07-20 12:21:00 +02:00
simplecpp : : Location loc ( files ) ;
loc . fileIndex = ~ 0U ;
loc . col = 0U ;
for ( unsigned int i = 0 ; i < files . size ( ) ; + + i ) {
if ( files [ i ] = = d1 . file ) {
loc . fileIndex = i ;
break ;
2011-10-24 19:51:00 +02:00
}
2016-07-20 12:21:00 +02:00
}
2011-10-24 19:51:00 +02:00
2016-07-20 12:21:00 +02:00
for ( unsigned int linenr = d1 . linenr + 1U ; linenr < d2 . linenr ; linenr + + ) {
loc . line = linenr ;
assemblerLocations . insert ( loc ) ;
}
}
2011-11-03 19:05:48 +01:00
2016-07-20 12:21:00 +02:00
unsigned int prevfile = 0 ;
unsigned int line = 1 ;
std : : ostringstream ret ;
for ( const simplecpp : : Token * tok = tokens2 . cbegin ( ) ; tok ; tok = tok - > next ) {
if ( writeLocations & & tok - > location . fileIndex ! = prevfile ) {
ret < < " \n #line " < < tok - > location . line < < " \" " < < tok - > location . file ( ) < < " \" \n " ;
prevfile = tok - > location . fileIndex ;
line = tok - > location . line ;
}
2011-11-03 19:05:48 +01:00
2016-07-20 12:21:00 +02:00
if ( tok - > previous & & line = = tok - > location . line )
ret < < ' ' ;
bool newline = false ;
while ( tok - > location . line > line ) {
ret < < ' \n ' ;
line + + ;
newline = true ;
}
if ( newline ) {
simplecpp : : Location loc = tok - > location ;
loc . col = 0U ;
if ( assemblerLocations . find ( loc ) ! = assemblerLocations . end ( ) ) {
ret < < " asm(); " ;
while ( assemblerLocations . find ( loc ) ! = assemblerLocations . end ( ) ) {
loc . line + + ;
}
while ( tok & & tok - > location . line < loc . line )
tok = tok - > next ;
if ( ! tok )
break ;
while ( line < tok - > location . line ) {
ret < < ' \n ' ;
+ + line ;
2011-11-03 19:05:48 +01:00
}
2016-07-20 12:21:00 +02:00
}
}
if ( ! tok - > macro . empty ( ) )
ret < < Preprocessor : : macroChar ;
ret < < tok - > str ;
}
2011-11-03 19:05:48 +01:00
2016-07-20 12:21:00 +02:00
return ret . str ( ) ;
}
2011-11-03 19:05:48 +01:00
2016-07-20 12:21:00 +02:00
std : : string Preprocessor : : getcode ( const std : : string & filedata , const std : : string & cfg , const std : : string & filename )
{
simplecpp : : OutputList outputList ;
std : : vector < std : : string > files ;
2013-08-18 18:04:06 +02:00
2016-07-20 12:21:00 +02:00
std : : istringstream istr ( filedata ) ;
const simplecpp : : TokenList & tokens1 = simplecpp : : TokenList ( istr , files , Path : : simplifyPath ( filename ) , & outputList ) ;
inlineSuppressions ( tokens1 ) ;
2011-11-03 19:05:48 +01:00
2016-07-20 12:21:00 +02:00
for ( simplecpp : : OutputList : : const_iterator it = outputList . begin ( ) ; it ! = outputList . end ( ) ; + + it ) {
2016-07-21 12:47:00 +02:00
switch ( it - > type ) {
case simplecpp : : Output : : ERROR :
2016-07-20 12:21:00 +02:00
error ( it - > location . file ( ) , it - > location . line , it - > msg ) ;
return " " ;
2016-07-21 12:47:00 +02:00
case simplecpp : : Output : : WARNING :
break ;
case simplecpp : : Output : : MISSING_INCLUDE : {
const std : : string : : size_type pos1 = it - > msg . find_first_of ( " < \" " ) ;
const std : : string : : size_type pos2 = it - > msg . find_first_of ( " > \" " , pos1 + 1U ) ;
if ( pos1 < pos2 & & pos2 ! = std : : string : : npos )
missingInclude ( it - > location . file ( ) , it - > location . line , it - > msg . substr ( pos1 + 1 , pos2 - pos1 - 1 ) , it - > msg [ pos1 ] = = ' \" ' ? UserHeader : SystemHeader ) ;
2011-10-17 20:12:46 +02:00
}
2016-07-21 12:47:00 +02:00
break ;
} ;
2011-10-17 20:12:46 +02:00
}
2016-07-20 12:21:00 +02:00
return getcode ( tokens1 , cfg , files , filedata . find ( " #file " ) ! = std : : string : : npos ) ;
2011-10-17 20:12:46 +02:00
}
2016-07-20 12:21:00 +02:00
void Preprocessor : : error ( const std : : string & filename , unsigned int linenr , const std : : string & msg )
2009-01-24 20:28:30 +01:00
{
2016-07-20 12:21:00 +02:00
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
if ( ! filename . empty ( ) ) {
ErrorLogger : : ErrorMessage : : FileLocation loc ( filename , linenr ) ;
locationList . push_back ( loc ) ;
2015-01-03 18:22:52 +01:00
}
2016-07-20 12:21:00 +02:00
_errorLogger - > reportErr ( ErrorLogger : : ErrorMessage ( locationList ,
Severity : : error ,
msg ,
" preprocessorErrorDirective " ,
false ) ) ;
}
2011-01-06 20:01:09 +01:00
2016-07-20 12:21:00 +02:00
Preprocessor : : HeaderTypes Preprocessor : : getHeaderFileName ( std : : string & str )
{
std : : string : : size_type i = str . find_first_of ( " < \" " ) ;
if ( i = = std : : string : : npos ) {
str = " " ;
return NoHeader ;
}
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
char c = str [ i ] ;
if ( c = = ' < ' )
c = ' > ' ;
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
std : : string result ;
for ( i = i + 1 ; i < str . length ( ) ; + + i ) {
if ( str [ i ] = = c )
break ;
2009-01-24 20:28:30 +01:00
2016-07-20 12:21:00 +02:00
result . append ( 1 , str [ i ] ) ;
}
2015-10-26 21:37:08 +01:00
2016-07-20 12:21:00 +02:00
// Linux can't open include paths with \ separator, so fix them
std : : replace ( result . begin ( ) , result . end ( ) , ' \\ ' , ' / ' ) ;
2010-02-09 21:26:15 +01:00
2016-07-20 12:21:00 +02:00
str = result ;
2010-09-03 13:30:49 +02:00
2016-07-20 12:21:00 +02:00
return ( c = = ' \" ' ) ? UserHeader : SystemHeader ;
2009-01-24 20:28:30 +01:00
}
2016-07-20 12:21:00 +02:00
2010-12-29 12:43:29 +01:00
// Report that include is missing
2013-03-13 06:48:33 +01:00
void Preprocessor : : missingInclude ( const std : : string & filename , unsigned int linenr , const std : : string & header , HeaderTypes headerType )
2010-12-29 12:43:29 +01:00
{
2014-05-03 19:31:15 +02:00
const std : : string fname = Path : : fromNativeSeparators ( filename ) ;
2015-07-24 13:30:41 +02:00
if ( _settings . nomsg . isSuppressed ( " missingInclude " , fname , linenr ) )
2014-05-03 19:31:15 +02:00
return ;
2015-07-24 13:30:41 +02:00
if ( headerType = = SystemHeader & & _settings . nomsg . isSuppressed ( " missingIncludeSystem " , fname , linenr ) )
2014-05-03 19:31:15 +02:00
return ;
2013-03-13 06:48:33 +01:00
2014-05-03 19:31:15 +02:00
if ( headerType = = SystemHeader )
missingSystemIncludeFlag = true ;
else
missingIncludeFlag = true ;
2015-07-24 13:30:41 +02:00
if ( _errorLogger & & _settings . checkConfiguration ) {
2014-05-03 19:31:15 +02:00
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
if ( ! filename . empty ( ) ) {
ErrorLogger : : ErrorMessage : : FileLocation loc ;
loc . line = linenr ;
loc . setfile ( Path : : toNativeSeparators ( filename ) ) ;
locationList . push_back ( loc ) ;
}
ErrorLogger : : ErrorMessage errmsg ( locationList , Severity : : information ,
( headerType = = SystemHeader ) ?
" Include file: < " + header + " > not found. Please note: Cppcheck does not need standard library headers to get proper results. " :
" Include file: \" " + header + " \" not found. " ,
( headerType = = SystemHeader ) ? " missingIncludeSystem " : " missingInclude " ,
false ) ;
errmsg . file0 = file0 ;
_errorLogger - > reportInfo ( errmsg ) ;
2010-12-29 12:43:29 +01:00
}
}
2012-07-10 20:29:04 +02:00
bool Preprocessor : : validateCfg ( const std : : string & code , const std : : string & cfg )
{
2015-07-24 13:30:41 +02:00
const bool printInformation = _settings . isEnabled ( " information " ) ;
2015-04-07 07:07:08 +02:00
2012-07-10 20:29:04 +02:00
// fill up "macros" with empty configuration macros
std : : set < std : : string > macros ;
2012-07-13 11:36:02 +02:00
for ( std : : string : : size_type pos = 0 ; pos < cfg . size ( ) ; ) {
2012-07-10 20:29:04 +02:00
const std : : string : : size_type pos2 = cfg . find_first_of ( " ;= " , pos ) ;
if ( pos2 = = std : : string : : npos ) {
macros . insert ( cfg . substr ( pos ) ) ;
break ;
}
if ( cfg [ pos2 ] = = ' ; ' )
macros . insert ( cfg . substr ( pos , pos2 - pos ) ) ;
2015-07-26 12:02:45 +02:00
pos = cfg . find ( ' ; ' , pos2 ) ;
2012-07-13 11:36:02 +02:00
if ( pos ! = std : : string : : npos )
+ + pos ;
2012-07-10 20:29:04 +02:00
}
// check if any empty macros are used in code
for ( std : : set < std : : string > : : const_iterator it = macros . begin ( ) ; it ! = macros . end ( ) ; + + it ) {
const std : : string & macro = * it ;
2012-07-11 18:29:33 +02:00
std : : string : : size_type pos = 0 ;
2012-07-11 21:20:31 +02:00
while ( ( pos = code . find_first_of ( std : : string ( " # \" ' " ) + macro [ 0 ] , pos ) ) ! = std : : string : : npos ) {
2012-07-11 18:29:33 +02:00
const std : : string : : size_type pos1 = pos ;
const std : : string : : size_type pos2 = pos + macro . size ( ) ;
pos + + ;
// skip string..
if ( code [ pos1 ] = = ' \" ' | | code [ pos1 ] = = ' \' ' ) {
while ( pos < code . size ( ) & & code [ pos ] ! = code [ pos1 ] ) {
if ( code [ pos ] = = ' \\ ' )
+ + pos ;
+ + pos ;
}
+ + pos ;
}
2012-07-11 21:20:31 +02:00
// skip preprocessor statement..
else if ( code [ pos1 ] = = ' # ' ) {
if ( pos1 = = 0 | | code [ pos1 - 1 ] = = ' \n ' )
2015-07-26 12:02:45 +02:00
pos = code . find ( ' \n ' , pos ) ;
2012-07-11 21:20:31 +02:00
}
2012-07-11 18:29:33 +02:00
// is macro used in code?
else if ( code . compare ( pos1 , macro . size ( ) , macro ) = = 0 ) {
2014-03-18 21:41:47 +01:00
if ( pos1 > 0 & & ( std : : isalnum ( ( unsigned char ) code [ pos1 - 1U ] ) | | code [ pos1 - 1U ] = = ' _ ' ) )
2012-07-11 18:29:33 +02:00
continue ;
2014-03-18 21:41:47 +01:00
if ( pos2 < code . size ( ) & & ( std : : isalnum ( ( unsigned char ) code [ pos2 ] ) | | code [ pos2 ] = = ' _ ' ) )
2012-07-11 18:29:33 +02:00
continue ;
// macro is used in code, return false
2015-04-07 07:07:08 +02:00
if ( printInformation )
2013-06-06 12:44:19 +02:00
validateCfgError ( cfg , macro ) ;
2012-07-11 18:29:33 +02:00
return false ;
}
2012-07-10 20:29:04 +02:00
}
}
return true ;
}
2013-06-06 12:44:19 +02:00
void Preprocessor : : validateCfgError ( const std : : string & cfg , const std : : string & macro )
2012-07-10 20:29:04 +02:00
{
const std : : string id = " ConfigurationNotChecked " ;
2012-11-03 11:25:40 +01:00
std : : list < ErrorLogger : : ErrorMessage : : FileLocation > locationList ;
2015-07-26 12:02:45 +02:00
ErrorLogger : : ErrorMessage : : FileLocation loc ( file0 , 1 ) ;
2012-11-03 11:25:40 +01:00
locationList . push_back ( loc ) ;
2013-06-26 09:47:02 +02:00
ErrorLogger : : ErrorMessage errmsg ( locationList , Severity : : information , " Skipping configuration ' " + cfg + " ' since the value of ' " + macro + " ' is unknown. Use -D if you want to check it. You can use -U to skip it explicitly. " , id , false ) ;
2012-07-10 20:29:04 +02:00
_errorLogger - > reportInfo ( errmsg ) ;
}
2010-12-29 12:43:29 +01:00
void Preprocessor : : getErrorMessages ( ErrorLogger * errorLogger , const Settings * settings )
2010-09-03 13:30:49 +02:00
{
2010-12-29 12:43:29 +01:00
Settings settings2 ( * settings ) ;
2015-07-24 13:30:41 +02:00
Preprocessor preprocessor ( settings2 , errorLogger ) ;
2013-03-13 19:50:50 +01:00
settings2 . checkConfiguration = true ;
2013-03-13 06:48:33 +01:00
preprocessor . missingInclude ( " " , 1 , " " , UserHeader ) ;
preprocessor . missingInclude ( " " , 1 , " " , SystemHeader ) ;
2013-06-06 12:44:19 +02:00
preprocessor . validateCfgError ( " X " , " X " ) ;
2010-12-29 12:43:29 +01:00
preprocessor . error ( " " , 1 , " #error message " ) ; // #error ..
2010-09-03 13:30:49 +02:00
}
2015-12-07 19:54:41 +01:00
void Preprocessor : : dump ( std : : ostream & out ) const
{
// Create a xml directive dump.
// The idea is not that this will be readable for humans. It's a
// data dump that 3rd party tools could load and get useful info from.
std : : list < Directive > : : const_iterator it ;
out < < " <directivelist> " < < std : : endl ;
for ( it = directives . begin ( ) ; it ! = directives . end ( ) ; + + it ) {
out < < " <directive "
< < " file= \" " < < it - > file < < " \" "
< < " linenr= \" " < < it - > linenr < < " \" "
// str might contain characters such as '"', '<' or '>' which
// could result in invalid XML, so run it through toxml().
< < " str= \" " < < ErrorLogger : : toxml ( it - > str ) < < " \" /> " < < std : : endl ;
}
out < < " </directivelist> " < < std : : endl ;
}