2013-09-21 14:42:35 +01:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
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
|
|
|
*/
|
|
|
|
|
2014-01-27 13:09:20 +01:00
|
|
|
SalLogAreas::SalLogAreas( const InstantiationData& data )
|
2016-04-26 15:50:12 +02:00
|
|
|
: Plugin(data), inFunction(nullptr)
|
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
|
|
|
}
|
|
|
|
|
2013-05-02 18:17:32 +02:00
|
|
|
bool SalLogAreas::VisitFunctionDecl( const FunctionDecl* function )
|
2012-10-13 17:38:58 +02:00
|
|
|
{
|
|
|
|
inFunction = function;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-05-02 18:17:32 +02:00
|
|
|
bool SalLogAreas::VisitCallExpr( const CallExpr* call )
|
2012-10-13 17:38:58 +02:00
|
|
|
{
|
|
|
|
if( ignoreLocation( call ))
|
|
|
|
return true;
|
2013-05-02 18:17:32 +02:00
|
|
|
if( const FunctionDecl* func = call->getDirectCallee())
|
2012-10-13 17:38:58 +02:00
|
|
|
{
|
|
|
|
// 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;
|
2015-11-19 16:05:35 +01:00
|
|
|
checkAreaSyntax(area, location);
|
2012-10-13 17:38:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-19 16:05:35 +01:00
|
|
|
void SalLogAreas::checkAreaSyntax(StringRef area, SourceLocation location) {
|
|
|
|
for (std::size_t i = 0;;) {
|
|
|
|
std::size_t j = area.find('.', i);
|
|
|
|
if (j == StringRef::npos) {
|
|
|
|
j = area.size();
|
|
|
|
}
|
|
|
|
if (j == i) {
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
for (; i != j; ++i) {
|
|
|
|
auto c = area[i];
|
|
|
|
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))) {
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (j == area.size()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
i = j + 1;
|
|
|
|
}
|
|
|
|
bad:
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
|
|
|
"invalid log area syntax '%0'%1 (see include/sal/log.hxx for details)",
|
|
|
|
location)
|
|
|
|
<< area << (location.isValid() ? "" : " in include/sal/log-areas.dox");
|
|
|
|
}
|
|
|
|
|
2012-10-13 17:38:58 +02:00
|
|
|
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 );
|
2015-11-19 16:05:35 +01:00
|
|
|
std::string area;
|
2012-10-13 17:38:58 +02:00
|
|
|
if( end == string::npos )
|
2015-11-19 16:05:35 +01:00
|
|
|
area = line.substr( pos );
|
2012-10-13 17:38:58 +02:00
|
|
|
else if( pos != end )
|
2015-11-19 16:05:35 +01:00
|
|
|
area = line.substr( pos, end - pos );
|
|
|
|
checkAreaSyntax(area, SourceLocation());
|
|
|
|
logAreas.insert(area);
|
2012-10-13 17:38:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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
|
2013-09-21 14:42:35 +01:00
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|