593f3b703c
I had it locally enabled for like a month now, and it did not produce any more noise than any of the other plugins, but quite some amount of malformed area designators had been introduced over time. Change-Id: I642591496bb9338246ba43a3d988481930c087fb
172 lines
6.1 KiB
C++
172 lines
6.1 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* 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"
|
|
|
|
#include <clang/Lex/Lexer.h>
|
|
|
|
#include <fstream>
|
|
|
|
namespace loplugin
|
|
{
|
|
|
|
/*
|
|
This is a compile check.
|
|
|
|
Check area used in SAL_INFO/SAL_WARN macros against the list in include/sal/log-areas.dox and
|
|
report if the area is not listed there. The fix is either use a proper area or add it to the list
|
|
if appropriate.
|
|
*/
|
|
|
|
SalLogAreas::SalLogAreas( const InstantiationData& data )
|
|
: Plugin( data )
|
|
{
|
|
}
|
|
|
|
void SalLogAreas::run()
|
|
{
|
|
inFunction = NULL;
|
|
lastSalDetailLogStreamMacro = SourceLocation();
|
|
TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
|
|
}
|
|
|
|
bool SalLogAreas::VisitFunctionDecl( const FunctionDecl* function )
|
|
{
|
|
inFunction = function;
|
|
return true;
|
|
}
|
|
|
|
bool SalLogAreas::VisitCallExpr( const CallExpr* call )
|
|
{
|
|
if( ignoreLocation( call ))
|
|
return true;
|
|
if( const FunctionDecl* func = call->getDirectCallee())
|
|
{
|
|
// Optimize, getQualifiedNameAsString() is reportedly expensive.
|
|
if( func->getNumParams() == 4 && func->getIdentifier() != NULL
|
|
&& ( func->getName() == "sal_detail_log" || func->getName() == "log" ))
|
|
{
|
|
string qualifiedName = func->getQualifiedNameAsString();
|
|
if( qualifiedName == "sal_detail_log" || qualifiedName == "sal::detail::log" )
|
|
{
|
|
// 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).
|
|
SourceLocation expansionLocation = compiler.getSourceManager().getExpansionLoc( call->getLocStart());
|
|
if( expansionLocation == lastSalDetailLogStreamMacro )
|
|
return true;
|
|
lastSalDetailLogStreamMacro = expansionLocation;
|
|
if( const StringLiteral* area = dyn_cast< StringLiteral >( call->getArg( 1 )->IgnoreParenImpCasts()))
|
|
{
|
|
if( area->getKind() == StringLiteral::Ascii )
|
|
checkArea( area->getBytes(), area->getExprLoc());
|
|
else
|
|
report( DiagnosticsEngine::Warning, "unsupported string literal kind (plugin needs fixing?)",
|
|
area->getLocStart());
|
|
return true;
|
|
}
|
|
if( inFunction->getQualifiedNameAsString() == "sal::detail::log" )
|
|
return true; // This function only forwards to sal_detail_log, so ok.
|
|
if( call->getArg( 1 )->isNullPointerConstant( compiler.getASTContext(),
|
|
Expr::NPC_ValueDependentIsNotNull ) != Expr::NPCK_NotNull )
|
|
{ // If the area argument is a null pointer, that is allowed only for SAL_DEBUG.
|
|
const SourceManager& source = compiler.getSourceManager();
|
|
for( SourceLocation loc = call->getLocStart();
|
|
loc.isMacroID();
|
|
loc = source.getImmediateExpansionRange( loc ).first )
|
|
{
|
|
StringRef inMacro = Lexer::getImmediateMacroName( loc, source, compiler.getLangOpts());
|
|
if( inMacro == "SAL_DEBUG" )
|
|
return true; // ok
|
|
}
|
|
report( DiagnosticsEngine::Warning, "missing log area",
|
|
call->getArg( 1 )->IgnoreParenImpCasts()->getLocStart());
|
|
return true;
|
|
}
|
|
report( DiagnosticsEngine::Warning, "cannot analyse log area argument (plugin needs fixing?)",
|
|
call->getLocStart());
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SalLogAreas::checkArea( StringRef area, SourceLocation location )
|
|
{
|
|
if( logAreas.empty())
|
|
readLogAreas();
|
|
if( !logAreas.count( area ))
|
|
{
|
|
report( DiagnosticsEngine::Warning, "unknown log area '%0' (check or extend include/sal/log-areas.dox)",
|
|
location ) << area;
|
|
checkAreaSyntax(area, location);
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
void SalLogAreas::readLogAreas()
|
|
{
|
|
ifstream is( SRCDIR "/include/sal/log-areas.dox" );
|
|
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 );
|
|
std::string area;
|
|
if( end == string::npos )
|
|
area = line.substr( pos );
|
|
else if( pos != end )
|
|
area = line.substr( pos, end - pos );
|
|
checkAreaSyntax(area, SourceLocation());
|
|
logAreas.insert(area);
|
|
}
|
|
}
|
|
// If you get this error message, you possibly have too old icecream (ICECC_EXTRAFILES is needed).
|
|
if( logAreas.empty())
|
|
report( DiagnosticsEngine::Warning, "error reading log areas" );
|
|
}
|
|
|
|
static Plugin::Registration< SalLogAreas > X( "sallogareas" );
|
|
|
|
} // namespace
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|