bump product version to 6.3.0.0.beta1
[LibreOffice.git] / compilerplugins / clang / sallogareas.cxx
blob50e25a3f04e66b4d8eca17f9d9032ec1baf36202
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * Based on LLVM/Clang.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
12 #include "sallogareas.hxx"
13 #include "check.hxx"
14 #include "compat.hxx"
16 #include <clang/Lex/Lexer.h>
18 #include <fstream>
20 namespace loplugin
24 This is a compile check.
26 Check area used in SAL_INFO/SAL_WARN macros against the list in include/sal/log-areas.dox and
27 report if the area is not listed there. The fix is either use a proper area or add it to the list
28 if appropriate.
31 SalLogAreas::SalLogAreas( const InstantiationData& data )
32 : FilteringPlugin(data), inFunction(nullptr)
36 void SalLogAreas::run()
38 inFunction = NULL;
39 lastSalDetailLogStreamMacro = SourceLocation();
40 TraverseDecl( compiler.getASTContext().getTranslationUnitDecl());
43 bool SalLogAreas::VisitFunctionDecl( const FunctionDecl* function )
45 inFunction = function;
46 return true;
49 bool SalLogAreas::VisitCallExpr( const CallExpr* call )
51 if( ignoreLocation( call ))
52 return true;
53 const FunctionDecl* func = call->getDirectCallee();
54 if( !func )
55 return true;
57 if( !( func->getNumParams() == 5 && func->getIdentifier() != NULL
58 && ( func->getName() == "sal_detail_log" || func->getName() == "log" || func->getName() == "DbgUnhandledException")) )
59 return true;
61 auto tc = loplugin::DeclCheck(func);
62 enum class LogCallKind { Sal, DbgUnhandledException};
63 LogCallKind kind;
64 int areaArgIndex;
65 if( tc.Function("sal_detail_log") || tc.Function("log").Namespace("detail").Namespace("sal").GlobalNamespace() )
67 kind = LogCallKind::Sal; // fine
68 areaArgIndex = 1;
70 else if( tc.Function("DbgUnhandledException").GlobalNamespace() )
72 kind = LogCallKind::DbgUnhandledException; // ok
73 areaArgIndex = 3;
75 else
76 return true;
78 // The SAL_DETAIL_LOG_STREAM macro expands to two calls to sal::detail::log(),
79 // so do not warn repeatedly about the same macro (the area->getLocStart() of all the calls
80 // from the same macro should be the same).
81 if( kind == LogCallKind::Sal )
83 SourceLocation expansionLocation = compiler.getSourceManager().getExpansionLoc( compat::getBeginLoc(call));
84 if( expansionLocation == lastSalDetailLogStreamMacro )
85 return true;
86 lastSalDetailLogStreamMacro = expansionLocation;
88 if( const clang::StringLiteral* area = dyn_cast< clang::StringLiteral >( call->getArg( areaArgIndex )->IgnoreParenImpCasts()))
90 if( area->getKind() == clang::StringLiteral::Ascii )
91 checkArea( area->getBytes(), area->getExprLoc());
92 else
93 report( DiagnosticsEngine::Warning, "unsupported string literal kind (plugin needs fixing?)",
94 compat::getBeginLoc(area));
95 return true;
97 if( loplugin::DeclCheck(inFunction).Function("log").Namespace("detail").Namespace("sal").GlobalNamespace()
98 || loplugin::DeclCheck(inFunction).Function("sal_detail_logFormat").GlobalNamespace() )
99 return true; // These functions only forward to sal_detail_log, so ok.
100 if( call->getArg( areaArgIndex )->isNullPointerConstant( compiler.getASTContext(),
101 Expr::NPC_ValueDependentIsNotNull ) != Expr::NPCK_NotNull )
102 { // If the area argument is a null pointer, that is allowed only for SAL_DEBUG.
103 const SourceManager& source = compiler.getSourceManager();
104 for( SourceLocation loc = compat::getBeginLoc(call);
105 loc.isMacroID();
106 loc = compat::getImmediateExpansionRange(source, loc ).first )
108 StringRef inMacro = Lexer::getImmediateMacroName( loc, source, compiler.getLangOpts());
109 if( inMacro == "SAL_DEBUG" || inMacro == "SAL_DEBUG_BACKTRACE" )
110 return true; // ok
112 report( DiagnosticsEngine::Warning, "missing log area",
113 compat::getBeginLoc(call->getArg( 1 )->IgnoreParenImpCasts()));
114 return true;
116 report( DiagnosticsEngine::Warning, "cannot analyse log area argument (plugin needs fixing?)",
117 compat::getBeginLoc(call));
118 return true;
121 void SalLogAreas::checkArea( StringRef area, SourceLocation location )
123 if( logAreas.empty())
124 readLogAreas();
125 if( !logAreas.count( area ))
127 report( DiagnosticsEngine::Warning, "unknown log area '%0' (check or extend include/sal/log-areas.dox)",
128 location ) << area;
129 checkAreaSyntax(area, location);
130 return;
132 // don't leave this alive by default, generates too many false+
133 #if 0
134 if (compiler.getSourceManager().isInMainFile(location))
136 auto matchpair = [this,area](StringRef p1, StringRef p2) {
137 return (area == p1 && firstSeenLogArea == p2) || (area == p2 && firstSeenLogArea == p1);
139 // these are "cross-module" log areas
140 if (area == "i18n" || area == "lok" || area == "lok.tiledrendering")
142 // these appear to be cross-file log areas
143 else if ( area == "chart2"
144 || area == "oox.cscode" || area == "oox.csdata"
145 || area == "slideshow.verbose"
146 || area == "sc.opencl"
147 || area == "sc.core.formulagroup"
148 || area == "sw.pageframe" || area == "sw.idle" || area == "sw.level2"
149 || area == "sw.docappend" || area == "sw.mailmerge"
150 || area == "sw.uno"
151 || area == "vcl.layout" || area == "vcl.a11y"
152 || area == "vcl.gdi.fontmetric" || area == "vcl.opengl"
153 || area == "vcl.harfbuzz" || area == "vcl.eventtesting"
154 || area == "vcl.schedule" || area == "vcl.unity"
155 || area == "xmlsecurity.comp"
158 else if (firstSeenLogArea == "")
160 firstSeenLogArea = area;
161 firstSeenLocation = location;
163 // some modules do this deliberately
164 else if (firstSeenLogArea.compare(0, 3, "jfw") == 0
165 || firstSeenLogArea.compare(0, 6, "opencl") == 0)
167 // mixing these in the same file seems legitimate
168 else if (
169 matchpair("chart2.pie.label.bestfit", "chart2.pie.label.bestfit.inside")
170 || matchpair("editeng", "editeng.chaining")
171 || matchpair("oox.drawingml", "oox.cscode")
172 || matchpair("oox.drawingml", "oox.drawingml.gradient")
173 || matchpair("sc.core", "sc.core.grouparealistener")
174 || matchpair("sc.orcus", "sc.orcus.condformat")
175 || matchpair("sc.orcus", "sc.orcus.style")
176 || matchpair("sc.orcus", "sc.orcus.autofilter")
177 || matchpair("svx", "svx.chaining")
178 || matchpair("sw.ww8", "sw.ww8.level2")
179 || matchpair("writerfilter", "writerfilter.profile")
182 else if (firstSeenLogArea != area)
184 report( DiagnosticsEngine::Warning, "two different log areas '%0' and '%1' in the same file?",
185 location ) << firstSeenLogArea << area;
186 report( DiagnosticsEngine::Note, "first area was seen here",
187 firstSeenLocation );
190 #endif
193 void SalLogAreas::checkAreaSyntax(StringRef area, SourceLocation location) {
194 for (std::size_t i = 0;;) {
195 std::size_t j = area.find('.', i);
196 if (j == StringRef::npos) {
197 j = area.size();
199 if (j == i) {
200 goto bad;
202 for (; i != j; ++i) {
203 auto c = area[i];
204 if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))) {
205 goto bad;
208 if (j == area.size()) {
209 return;
211 i = j + 1;
213 bad:
214 report(
215 DiagnosticsEngine::Warning,
216 "invalid log area syntax '%0'%1 (see include/sal/log.hxx for details)",
217 location)
218 << area << (location.isValid() ? "" : " in include/sal/log-areas.dox");
221 void SalLogAreas::readLogAreas()
223 std::ifstream is( SRCDIR "/include/sal/log-areas.dox" );
224 while( is.good())
226 std::string line;
227 getline( is, line );
228 size_t pos = line.find( "@li @c " );
229 if( pos != std::string::npos )
231 pos += strlen( "@li @c " );
232 size_t end = line.find( ' ', pos );
233 std::string area;
234 if( end == std::string::npos )
235 area = line.substr( pos );
236 else if( pos != end )
237 area = line.substr( pos, end - pos );
238 checkAreaSyntax(area, SourceLocation());
239 logAreas.insert(area);
242 // If you get this error message, you possibly have too old icecream (ICECC_EXTRAFILES is needed).
243 if( logAreas.empty())
244 report( DiagnosticsEngine::Warning, "error reading log areas" );
247 static Plugin::Registration< SalLogAreas > X( "sallogareas" );
249 } // namespace
251 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */