2 * This file is part of the LibreOffice project.
6 * This file is distributed under the University of Illinois Open Source
7 * License. See LICENSE.TXT for details.
11 #include "sallogareas.hxx"
13 #include <clang/AST/ASTContext.h>
14 #include <clang/Basic/SourceManager.h>
15 #include <clang/Lex/Lexer.h>
23 This is a compile check.
25 Check that areas used in SAL_LOG/SAL_WARN are listed in sal/inc/sal/log-areas.dox .
28 SalLogAreas::SalLogAreas( ASTContext
& context
)
33 void SalLogAreas::run()
36 lastSalDetailLogStreamMacro
= SourceLocation();
37 TraverseDecl( context
.getTranslationUnitDecl());
40 bool SalLogAreas::VisitFunctionDecl( FunctionDecl
* function
)
42 inFunction
= function
;
46 bool SalLogAreas::VisitCallExpr( CallExpr
* call
)
48 if( ignoreLocation( call
))
50 if( FunctionDecl
* func
= call
->getDirectCallee())
52 // Optimize, getQualifiedNameAsString() is reportedly expensive.
53 if( func
->getNumParams() == 4 && func
->getIdentifier() != NULL
54 && ( func
->getName() == "sal_detail_log" || func
->getName() == "log" ))
56 string qualifiedName
= func
->getQualifiedNameAsString();
57 if( qualifiedName
== "sal_detail_log" || qualifiedName
== "sal::detail::log" )
59 // The SAL_DETAIL_LOG_STREAM macro expands to two calls to sal::detail::log(),
60 // so do not warn repeatedly about the same macro (the area->getLocStart() of all the calls
61 // from the same macro should be the same).
62 SourceLocation expansionLocation
= context
.getSourceManager().getExpansionLoc( call
->getLocStart());
63 if( expansionLocation
== lastSalDetailLogStreamMacro
)
65 lastSalDetailLogStreamMacro
= expansionLocation
;
66 if( const StringLiteral
* area
= dyn_cast
< StringLiteral
>( call
->getArg( 1 )->IgnoreParenImpCasts()))
68 if( area
->getKind() == StringLiteral::Ascii
)
69 checkArea( area
->getBytes(), area
->getExprLoc());
71 report( DiagnosticsEngine::Warning
, "unsupported string literal kind (plugin needs fixing?) [loplugin]",
75 if( inFunction
->getQualifiedNameAsString() == "sal::detail::log" )
76 return true; // This function only forwards to sal_detail_log, so ok.
77 if( call
->getArg( 1 )->isNullPointerConstant( context
, Expr::NPC_ValueDependentIsNotNull
) != Expr::NPCK_NotNull
)
78 { // If the area argument is a null pointer, that is allowed only for SAL_DEBUG.
79 const SourceManager
& source
= context
.getSourceManager();
80 for( SourceLocation loc
= call
->getLocStart();
82 loc
= source
.getImmediateExpansionRange( loc
).first
)
84 StringRef inMacro
= Lexer::getImmediateMacroName( loc
, source
, context
.getLangOpts());
85 if( inMacro
== "SAL_DEBUG" )
88 report( DiagnosticsEngine::Warning
, "missing log area [loplugin]",
89 call
->getArg( 1 )->IgnoreParenImpCasts()->getLocStart());
92 report( DiagnosticsEngine::Warning
, "cannot analyse log area argument (plugin needs fixing?) [loplugin]",
100 void SalLogAreas::checkArea( StringRef area
, SourceLocation location
)
102 if( logAreas
.empty())
104 if( !logAreas
.count( area
))
106 report( DiagnosticsEngine::Warning
, "unknown log area '%0' (check or extend sal/inc/sal/log-areas.dox) [loplugin]",
111 void SalLogAreas::readLogAreas()
113 #define STRINGIFY2( s ) #s
114 #define STRINGIFY( s ) STRINGIFY2( s )
115 ifstream
is( STRINGIFY( SRCDIR
) "/sal/inc/sal/log-areas.dox" );
122 size_t pos
= line
.find( "@li @c " );
123 if( pos
!= string::npos
)
125 pos
+= strlen( "@li @c " );
126 size_t end
= line
.find( ' ', pos
);
127 if( end
== string::npos
)
128 logAreas
.insert( line
.substr( pos
));
129 else if( pos
!= end
)
130 logAreas
.insert( line
.substr( pos
, end
- pos
));
133 // If you get this error message, you possibly have too old icecream (ICECC_EXTRAFILES is needed).
134 if( logAreas
.empty())
135 report( DiagnosticsEngine::Warning
, "error reading log areas [loplugin]" );