2012-10-13 17:38:58 +02:00
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* Based on LLVM/Clang.
|
|
|
|
*
|
|
|
|
* This file is distributed under the University of Illinois Open Source
|
|
|
|
* License. See LICENSE.TXT for details.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "sallogareas.hxx"
|
|
|
|
|
2012-10-26 19:24:24 +02:00
|
|
|
#include <clang/Lex/Lexer.h>
|
2012-10-13 17:38:58 +02:00
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
namespace loplugin
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
2012-10-15 15:36:25 +02:00
|
|
|
This is a compile check.
|
|
|
|
|
2013-04-24 11:42:09 +03:00
|
|
|
Check area used in SAL_INFO/SAL_WARN macros against the list in include/sal/log-areas.dox and
|
2013-02-02 18:34:12 +01:00
|
|
|
report if the area is not listed there. The fix is either use a proper area or add it to the list
|
|
|
|
if appropriate.
|
2012-10-13 17:38:58 +02:00
|
|
|
*/
|
|
|
|
|
2013-03-21 16:42:10 +01:00
|
|
|
SalLogAreas::SalLogAreas( CompilerInstance& compiler )
|
|
|
|
: Plugin( compiler )
|
2012-10-13 17:38:58 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SalLogAreas::run()
|
|
|
|
{
|
|
|
|
inFunction = NULL;
|
2012-10-17 19:53:32 +02:00
|
|
|
lastSalDetailLogStreamMacro = SourceLocation();
|
2013-03-21 16:42:10 +01:00
|
|
|
TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
|
2012-10-13 17:38:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool SalLogAreas::VisitFunctionDecl( FunctionDecl* function )
|
|
|
|
{
|
|
|
|
inFunction = function;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SalLogAreas::VisitCallExpr( CallExpr* call )
|
|
|
|
{
|
|
|
|
if( ignoreLocation( call ))
|
|
|
|
return true;
|
|
|
|
if( FunctionDecl* func = call->getDirectCallee())
|
|
|
|
{
|
|
|
|
// Optimize, getQualifiedNameAsString() is reportedly expensive.
|
|
|
|
if( func->getNumParams() == 4 && func->getIdentifier() != NULL
|
|
|
|
&& ( func->getName() == "sal_detail_log" || func->getName() == "log" ))
|
|
|
|
{
|
2012-10-15 14:58:19 +02:00
|
|
|
string qualifiedName = func->getQualifiedNameAsString();
|
2012-10-13 17:38:58 +02:00
|
|
|
if( qualifiedName == "sal_detail_log" || qualifiedName == "sal::detail::log" )
|
|
|
|
{
|
2012-10-26 19:24:24 +02:00
|
|
|
// The SAL_DETAIL_LOG_STREAM macro expands to two calls to sal::detail::log(),
|
|
|
|
// so do not warn repeatedly about the same macro (the area->getLocStart() of all the calls
|
|
|
|
// from the same macro should be the same).
|
2013-03-21 16:42:10 +01:00
|
|
|
SourceLocation expansionLocation = compiler.getSourceManager().getExpansionLoc( call->getLocStart());
|
2012-10-26 19:24:24 +02:00
|
|
|
if( expansionLocation == lastSalDetailLogStreamMacro )
|
|
|
|
return true;
|
|
|
|
lastSalDetailLogStreamMacro = expansionLocation;
|
2012-10-13 17:38:58 +02:00
|
|
|
if( const StringLiteral* area = dyn_cast< StringLiteral >( call->getArg( 1 )->IgnoreParenImpCasts()))
|
|
|
|
{
|
|
|
|
if( area->getKind() == StringLiteral::Ascii )
|
|
|
|
checkArea( area->getBytes(), area->getExprLoc());
|
|
|
|
else
|
2013-02-02 19:38:56 +01:00
|
|
|
report( DiagnosticsEngine::Warning, "unsupported string literal kind (plugin needs fixing?)",
|
2012-10-13 17:38:58 +02:00
|
|
|
area->getLocStart());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if( inFunction->getQualifiedNameAsString() == "sal::detail::log" )
|
|
|
|
return true; // This function only forwards to sal_detail_log, so ok.
|
2013-03-21 16:42:10 +01:00
|
|
|
if( call->getArg( 1 )->isNullPointerConstant( compiler.getASTContext(),
|
|
|
|
Expr::NPC_ValueDependentIsNotNull ) != Expr::NPCK_NotNull )
|
2012-10-26 19:24:24 +02:00
|
|
|
{ // If the area argument is a null pointer, that is allowed only for SAL_DEBUG.
|
2013-03-21 16:42:10 +01:00
|
|
|
const SourceManager& source = compiler.getSourceManager();
|
2012-10-26 19:24:24 +02:00
|
|
|
for( SourceLocation loc = call->getLocStart();
|
|
|
|
loc.isMacroID();
|
|
|
|
loc = source.getImmediateExpansionRange( loc ).first )
|
|
|
|
{
|
2013-03-21 16:42:10 +01:00
|
|
|
StringRef inMacro = Lexer::getImmediateMacroName( loc, source, compiler.getLangOpts());
|
2012-10-26 19:24:24 +02:00
|
|
|
if( inMacro == "SAL_DEBUG" )
|
|
|
|
return true; // ok
|
|
|
|
}
|
2013-02-02 19:38:56 +01:00
|
|
|
report( DiagnosticsEngine::Warning, "missing log area",
|
2012-10-26 19:24:24 +02:00
|
|
|
call->getArg( 1 )->IgnoreParenImpCasts()->getLocStart());
|
|
|
|
return true;
|
|
|
|
}
|
2013-02-02 19:38:56 +01:00
|
|
|
report( DiagnosticsEngine::Warning, "cannot analyse log area argument (plugin needs fixing?)",
|
2012-10-13 17:38:58 +02:00
|
|
|
call->getLocStart());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SalLogAreas::checkArea( StringRef area, SourceLocation location )
|
|
|
|
{
|
|
|
|
if( logAreas.empty())
|
|
|
|
readLogAreas();
|
|
|
|
if( !logAreas.count( area ))
|
|
|
|
{
|
2013-04-24 11:42:09 +03:00
|
|
|
report( DiagnosticsEngine::Warning, "unknown log area '%0' (check or extend include/sal/log-areas.dox)",
|
2012-10-13 17:38:58 +02:00
|
|
|
location ) << area;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SalLogAreas::readLogAreas()
|
|
|
|
{
|
2013-04-24 11:42:09 +03:00
|
|
|
ifstream is( SRCDIR "/include/sal/log-areas.dox" );
|
2012-10-13 17:38:58 +02:00
|
|
|
while( is.good())
|
|
|
|
{
|
|
|
|
string line;
|
|
|
|
getline( is, line );
|
|
|
|
size_t pos = line.find( "@li @c " );
|
|
|
|
if( pos != string::npos )
|
|
|
|
{
|
|
|
|
pos += strlen( "@li @c " );
|
|
|
|
size_t end = line.find( ' ', pos );
|
|
|
|
if( end == string::npos )
|
|
|
|
logAreas.insert( line.substr( pos ));
|
|
|
|
else if( pos != end )
|
|
|
|
logAreas.insert( line.substr( pos, end - pos ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If you get this error message, you possibly have too old icecream (ICECC_EXTRAFILES is needed).
|
|
|
|
if( logAreas.empty())
|
2013-02-02 19:38:56 +01:00
|
|
|
report( DiagnosticsEngine::Warning, "error reading log areas" );
|
2012-10-13 17:38:58 +02:00
|
|
|
}
|
|
|
|
|
2013-02-02 17:45:18 +01:00
|
|
|
static Plugin::Registration< SalLogAreas > X( "sallogareas" );
|
|
|
|
|
2012-10-13 17:38:58 +02:00
|
|
|
} // namespace
|