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/Lex/Lexer.h>
21 This is a compile check.
23 Check area used in SAL_INFO/SAL_WARN macros against the list in include/sal/log-areas.dox and
24 report if the area is not listed there. The fix is either use a proper area or add it to the list
28 SalLogAreas::SalLogAreas( CompilerInstance
& compiler
)
33 void SalLogAreas::run()
36 lastSalDetailLogStreamMacro
= SourceLocation();
37 TraverseDecl( compiler
.getASTContext().getTranslationUnitDecl());
40 bool SalLogAreas::VisitFunctionDecl( const FunctionDecl
* function
)
42 inFunction
= function
;
46 bool SalLogAreas::VisitCallExpr( const CallExpr
* call
)
48 if( ignoreLocation( call
))
50 if( const 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
= compiler
.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?)",
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( compiler
.getASTContext(),
78 Expr::NPC_ValueDependentIsNotNull
) != Expr::NPCK_NotNull
)
79 { // If the area argument is a null pointer, that is allowed only for SAL_DEBUG.
80 const SourceManager
& source
= compiler
.getSourceManager();
81 for( SourceLocation loc
= call
->getLocStart();
83 loc
= source
.getImmediateExpansionRange( loc
).first
)
85 StringRef inMacro
= Lexer::getImmediateMacroName( loc
, source
, compiler
.getLangOpts());
86 if( inMacro
== "SAL_DEBUG" )
89 report( DiagnosticsEngine::Warning
, "missing log area",
90 call
->getArg( 1 )->IgnoreParenImpCasts()->getLocStart());
93 report( DiagnosticsEngine::Warning
, "cannot analyse log area argument (plugin needs fixing?)",
101 void SalLogAreas::checkArea( StringRef area
, SourceLocation location
)
103 if( logAreas
.empty())
105 if( !logAreas
.count( area
))
107 report( DiagnosticsEngine::Warning
, "unknown log area '%0' (check or extend include/sal/log-areas.dox)",
112 void SalLogAreas::readLogAreas()
114 ifstream
is( SRCDIR
"/include/sal/log-areas.dox" );
119 size_t pos
= line
.find( "@li @c " );
120 if( pos
!= string::npos
)
122 pos
+= strlen( "@li @c " );
123 size_t end
= line
.find( ' ', pos
);
124 if( end
== string::npos
)
125 logAreas
.insert( line
.substr( pos
));
126 else if( pos
!= end
)
127 logAreas
.insert( line
.substr( pos
, end
- pos
));
130 // If you get this error message, you possibly have too old icecream (ICECC_EXTRAFILES is needed).
131 if( logAreas
.empty())
132 report( DiagnosticsEngine::Warning
, "error reading log areas" );
135 static Plugin::Registration
< SalLogAreas
> X( "sallogareas" );