Version 4.0.2.1, tag libreoffice-4.0.2.1
[LibreOffice.git] / compilerplugins / clang / sallogareas.cxx
blob10630aa3981cd636175f3021f33a23199299b1e1
1 /*
2 * This file is part of the LibreOffice project.
4 * Based on LLVM/Clang.
6 * This file is distributed under the University of Illinois Open Source
7 * License. See LICENSE.TXT for details.
9 */
11 #include "sallogareas.hxx"
13 #include <clang/AST/ASTContext.h>
14 #include <clang/Basic/SourceManager.h>
15 #include <clang/Lex/Lexer.h>
17 #include <fstream>
19 namespace loplugin
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 )
29 : Plugin( context )
33 void SalLogAreas::run()
35 inFunction = NULL;
36 lastSalDetailLogStreamMacro = SourceLocation();
37 TraverseDecl( context.getTranslationUnitDecl());
40 bool SalLogAreas::VisitFunctionDecl( FunctionDecl* function )
42 inFunction = function;
43 return true;
46 bool SalLogAreas::VisitCallExpr( CallExpr* call )
48 if( ignoreLocation( call ))
49 return true;
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 )
64 return true;
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());
70 else
71 report( DiagnosticsEngine::Warning, "unsupported string literal kind (plugin needs fixing?) [loplugin]",
72 area->getLocStart());
73 return true;
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();
81 loc.isMacroID();
82 loc = source.getImmediateExpansionRange( loc ).first )
84 StringRef inMacro = Lexer::getImmediateMacroName( loc, source, context.getLangOpts());
85 if( inMacro == "SAL_DEBUG" )
86 return true; // ok
88 report( DiagnosticsEngine::Warning, "missing log area [loplugin]",
89 call->getArg( 1 )->IgnoreParenImpCasts()->getLocStart());
90 return true;
92 report( DiagnosticsEngine::Warning, "cannot analyse log area argument (plugin needs fixing?) [loplugin]",
93 call->getLocStart());
97 return true;
100 void SalLogAreas::checkArea( StringRef area, SourceLocation location )
102 if( logAreas.empty())
103 readLogAreas();
104 if( !logAreas.count( area ))
106 report( DiagnosticsEngine::Warning, "unknown log area '%0' (check or extend sal/inc/sal/log-areas.dox) [loplugin]",
107 location ) << area;
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" );
116 #undef STRINGIFY
117 #undef STRINGIFY2
118 while( is.good())
120 string line;
121 getline( is, line );
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]" );
138 } // namespace