1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10 #include "plugin.hxx"
11 #include "check.hxx"
12 #include "compat.hxx"
13
14 #include <algorithm>
15 #include <cassert>
16 #include <set>
17 #include <utility>
18 #include <vector>
19
20 // The SAL_CALL function annotation is only necessary on our outward
21 // facing C++ ABI, anywhere else it is just cargo-cult.
22 //
23
24 //TODO: To find inconsistencies like
25 //
26 // template<typename> struct S { void f(); }; // #1
27 // template<typename T> void S<T>::f() {} // #2
28 // template void SAL_CALL S<void>::f();
29 //
30 // VisitFunctionDecl would need to also visit explicit instantiations, by letting
31 // shouldVisitTemplateInstantiations return true and returning from VisitFunctionDecl early iff
32 // decl->getTemplateSpecializationKind() == TSK_ImplicitInstantiation. However, an instantiatied
33 // FunctionDecl is created in TemplateDeclInstantiator::VisitCXXMethodDecl by copying information
34 // (including source locations) from the declaration at #1, and later modified in
35 // Sema::InstantiateFunctionDefinition with some source location information from the definition at
36 // #2. That means that the source scanning in isSalCallFunction below would be thoroughly confused
37 // and break. (This happens for both explicit and implicit template instantiations, which is the
38 // reason why calls to isSalCallFunction make sure to not call it with any FunctionDecls
39 // representing such template instantiations.)
40
41 namespace
42 {
43 //static bool startswith(const std::string& rStr, const char* pSubStr)
44 //{
45 // return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
46 //}
47
getTemplateInstantiationPattern(CXXMethodDecl const * decl)48 CXXMethodDecl const* getTemplateInstantiationPattern(CXXMethodDecl const* decl)
49 {
50 auto const p = decl->getTemplateInstantiationPattern();
51 return p == nullptr ? decl : cast<CXXMethodDecl>(p);
52 }
53
54 class SalCall final : public loplugin::FilteringRewritePlugin<SalCall>
55 {
56 public:
SalCall(loplugin::InstantiationData const & data)57 explicit SalCall(loplugin::InstantiationData const& data)
58 : FilteringRewritePlugin(data)
59 {
60 }
61
run()62 virtual void run() override
63 {
64 m_phase = PluginPhase::FindAddressOf;
65 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
66 m_phase = PluginPhase::Warning;
67 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
68 }
69
70 bool VisitFunctionDecl(FunctionDecl const*);
71 bool VisitUnaryAddrOf(UnaryOperator const*);
72 bool VisitInitListExpr(InitListExpr const*);
73 bool VisitCallExpr(CallExpr const*);
74 bool VisitBinAssign(BinaryOperator const*);
75 bool VisitCXXConstructExpr(CXXConstructExpr const*);
76
77 private:
78 void checkForFunctionDecl(Expr const*, bool bCheckOnly = false);
79 bool rewrite(SourceLocation);
80 bool isSalCallFunction(FunctionDecl const* functionDecl, SourceLocation* pLoc = nullptr);
81
82 std::set<FunctionDecl const*> m_addressOfSet;
83 enum class PluginPhase
84 {
85 FindAddressOf,
86 Warning
87 };
88 PluginPhase m_phase;
89 };
90
VisitUnaryAddrOf(UnaryOperator const * op)91 bool SalCall::VisitUnaryAddrOf(UnaryOperator const* op)
92 {
93 if (m_phase != PluginPhase::FindAddressOf)
94 return true;
95 checkForFunctionDecl(op->getSubExpr());
96 return true;
97 }
98
VisitBinAssign(BinaryOperator const * binaryOperator)99 bool SalCall::VisitBinAssign(BinaryOperator const* binaryOperator)
100 {
101 if (m_phase != PluginPhase::FindAddressOf)
102 return true;
103 checkForFunctionDecl(binaryOperator->getRHS());
104 return true;
105 }
106
VisitCallExpr(CallExpr const * callExpr)107 bool SalCall::VisitCallExpr(CallExpr const* callExpr)
108 {
109 if (m_phase != PluginPhase::FindAddressOf)
110 return true;
111 for (auto arg : callExpr->arguments())
112 checkForFunctionDecl(arg);
113 return true;
114 }
115
VisitCXXConstructExpr(CXXConstructExpr const * constructExpr)116 bool SalCall::VisitCXXConstructExpr(CXXConstructExpr const* constructExpr)
117 {
118 if (m_phase != PluginPhase::FindAddressOf)
119 return true;
120 for (auto arg : constructExpr->arguments())
121 checkForFunctionDecl(arg);
122 return true;
123 }
124
VisitInitListExpr(InitListExpr const * initListExpr)125 bool SalCall::VisitInitListExpr(InitListExpr const* initListExpr)
126 {
127 if (m_phase != PluginPhase::FindAddressOf)
128 return true;
129 for (auto subStmt : *initListExpr)
130 checkForFunctionDecl(dyn_cast<Expr>(subStmt));
131 return true;
132 }
133
checkForFunctionDecl(Expr const * expr,bool bCheckOnly)134 void SalCall::checkForFunctionDecl(Expr const* expr, bool bCheckOnly)
135 {
136 auto e1 = expr->IgnoreParenCasts();
137 auto declRef = dyn_cast<DeclRefExpr>(e1);
138 if (!declRef)
139 return;
140 auto functionDecl = dyn_cast<FunctionDecl>(declRef->getDecl());
141 if (!functionDecl)
142 return;
143 if (bCheckOnly)
144 getParentStmt(expr)->dump();
145 else
146 m_addressOfSet.insert(functionDecl->getCanonicalDecl());
147 }
148
VisitFunctionDecl(FunctionDecl const * decl)149 bool SalCall::VisitFunctionDecl(FunctionDecl const* decl)
150 {
151 if (m_phase != PluginPhase::Warning)
152 return true;
153 if (ignoreLocation(decl))
154 return true;
155
156 // ignore template stuff
157 if (decl->getTemplatedKind() != clang::FunctionDecl::TK_NonTemplate)
158 return true;
159 auto recordDecl = dyn_cast<CXXRecordDecl>(decl->getDeclContext());
160 if (recordDecl
161 && (recordDecl->getTemplateSpecializationKind() != TSK_Undeclared
162 || recordDecl->isDependentContext()))
163 {
164 return true;
165 }
166
167 auto canonicalDecl = decl->getCanonicalDecl();
168
169 // ignore UNO implementations
170 if (isInUnoIncludeFile(
171 compiler.getSourceManager().getSpellingLoc(canonicalDecl->getLocation())))
172 return true;
173
174 SourceLocation rewriteLoc;
175 SourceLocation rewriteCanonicalLoc;
176 bool bDeclIsSalCall = isSalCallFunction(decl, &rewriteLoc);
177 bool bCanonicalDeclIsSalCall = isSalCallFunction(canonicalDecl, &rewriteCanonicalLoc);
178
179 // first, check for consistency, so we don't trip ourselves up on Linux, where we normally run the plugin
180 if (canonicalDecl != decl)
181 {
182 if (bCanonicalDeclIsSalCall)
183 ; // this is fine, the actual definition have or not have SAL_CALL, and MSVC is fine with it
184 else if (bDeclIsSalCall)
185 {
186 // not fine
187 report(DiagnosticsEngine::Warning, "SAL_CALL inconsistency", decl->getLocation())
188 << decl->getSourceRange();
189 report(DiagnosticsEngine::Note, "SAL_CALL inconsistency", canonicalDecl->getLocation())
190 << canonicalDecl->getSourceRange();
191 return true;
192 }
193 }
194 auto methodDecl = dyn_cast<CXXMethodDecl>(canonicalDecl);
195 if (methodDecl)
196 {
197 for (auto iter = methodDecl->begin_overridden_methods();
198 iter != methodDecl->end_overridden_methods(); ++iter)
199 {
200 const CXXMethodDecl* overriddenMethod
201 = getTemplateInstantiationPattern(*iter)->getCanonicalDecl();
202 if (bCanonicalDeclIsSalCall != isSalCallFunction(overriddenMethod))
203 {
204 report(DiagnosticsEngine::Warning, "SAL_CALL inconsistency",
205 methodDecl->getLocation())
206 << methodDecl->getSourceRange();
207 report(DiagnosticsEngine::Note, "SAL_CALL inconsistency",
208 overriddenMethod->getLocation())
209 << overriddenMethod->getSourceRange();
210 return true;
211 }
212 }
213 }
214
215 if (!bCanonicalDeclIsSalCall)
216 return true;
217
218 if (!decl->isThisDeclarationADefinition() && !(methodDecl && methodDecl->isPure()))
219 return true;
220 // can only check when we have a definition since this is the most likely time
221 // when the address of the method will be taken
222 if (methodDecl)
223 {
224 if (m_addressOfSet.find(decl->getCanonicalDecl()) != m_addressOfSet.end())
225 return true;
226 }
227
228 // some base classes are overridden by sub-classes which override both the base-class and a UNO class
229 if (recordDecl)
230 {
231 auto dc = loplugin::DeclCheck(recordDecl);
232 if (dc.Class("OProxyAggregation").Namespace("comphelper").GlobalNamespace()
233 || dc.Class("OComponentProxyAggregationHelper")
234 .Namespace("comphelper")
235 .GlobalNamespace()
236 || dc.Class("SvxShapeMaster").GlobalNamespace()
237 || dc.Class("ListBoxAccessibleBase").Namespace("accessibility").GlobalNamespace()
238 || dc.Class("AsyncEventNotifierBase").Namespace("comphelper").GlobalNamespace()
239 || dc.Class("ODescriptor")
240 .Namespace("sdbcx")
241 .Namespace("connectivity")
242 .GlobalNamespace()
243 || dc.Class("IController").Namespace("dbaui").GlobalNamespace()
244 || dc.Class("ORowSetBase").Namespace("dbaccess").GlobalNamespace()
245 || dc.Class("OComponentAdapterBase").Namespace("bib").GlobalNamespace()
246 || dc.Class("IEventProcessor").Namespace("comphelper").GlobalNamespace()
247 || dc.Class("SvxUnoTextBase").GlobalNamespace()
248 || dc.Class("OInterfaceContainer").Namespace("frm").GlobalNamespace()
249 || dc.Class("AccessibleComponentBase").Namespace("accessibility").GlobalNamespace()
250 || dc.Class("ContextHandler2Helper")
251 .Namespace("core")
252 .Namespace("oox")
253 .GlobalNamespace()
254 || dc.Class("AccessibleStaticTextBase").Namespace("accessibility").GlobalNamespace()
255 || dc.Class("OCommonPicker").Namespace("svt").GlobalNamespace()
256 || dc.Class("VbaDocumentBase").GlobalNamespace()
257 || dc.Class("VbaPageSetupBase").GlobalNamespace()
258 || dc.Class("ScVbaControl").GlobalNamespace()
259
260 )
261 return true;
262 }
263
264 // if any of the overridden methods are SAL_CALL, we should be too
265 if (methodDecl)
266 {
267 for (auto iter = methodDecl->begin_overridden_methods();
268 iter != methodDecl->end_overridden_methods(); ++iter)
269 {
270 const CXXMethodDecl* overriddenMethod
271 = getTemplateInstantiationPattern(*iter)->getCanonicalDecl();
272 if (isSalCallFunction(overriddenMethod))
273 return true;
274 }
275 }
276
277 bool bOK = rewrite(rewriteLoc);
278 if (bOK && canonicalDecl != decl)
279 {
280 bOK = rewrite(rewriteCanonicalLoc);
281 }
282 if (bOK)
283 return true;
284
285 if (bDeclIsSalCall)
286 {
287 report(DiagnosticsEngine::Warning, "SAL_CALL unnecessary here",
288 rewriteLoc.isValid() ? rewriteLoc : decl->getLocation())
289 << decl->getSourceRange();
290 }
291 if (canonicalDecl != decl)
292 {
293 report(DiagnosticsEngine::Warning, "SAL_CALL unnecessary here", rewriteCanonicalLoc)
294 << canonicalDecl->getSourceRange();
295 if (!bDeclIsSalCall)
296 {
297 report(DiagnosticsEngine::Note, "defined here (without SAL_CALL decoration)",
298 decl->getLocation())
299 << decl->getSourceRange();
300 }
301 }
302
303 return true;
304 }
305
306 //TODO: This doesn't handle all possible cases of macro usage (and possibly never will be able to),
307 // just what is encountered in practice:
isSalCallFunction(FunctionDecl const * functionDecl,SourceLocation * pLoc)308 bool SalCall::isSalCallFunction(FunctionDecl const* functionDecl, SourceLocation* pLoc)
309 {
310 assert(!functionDecl->isTemplateInstantiation());
311
312 //TODO: It appears that FunctionDecls representing explicit template specializations have the
313 // same issue as those representing (implicit or explicit) instantiations, namely that their
314 // data (including relevant source locations) is an incoherent combination of data from the
315 // original template declaration and the later specialization definition. For example, for the
316 // OValueLimitedType<double>::registerProperties specialization at
317 // forms/source/xforms/datatyperepository.cxx:241, the FunctionDecl (which is even considered
318 // canonic) representing the base-class function overridden by ODecimalType::registerProperties
319 // (forms/source/xforms/datatypes.hxx:299) is dumped as
320 //
321 // CXXMethodDecl <forms/source/xforms/datatypes.hxx:217:9, col:54>
322 // forms/source/xforms/datatyperepository.cxx:242:37 registerProperties 'void (void)' virtual
323 //
324 // mixing the source range ("datatypes.hxx:217:9, col:54") from the original declaration with
325 // the name location ("datatyperepository.cxx:242:37") from the explicit specialization. Just
326 // give up for now and assume no "SAL_CALL" is present:
327 if (functionDecl->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
328 {
329 return false;
330 }
331
332 SourceManager& SM = compiler.getSourceManager();
333 std::vector<SourceRange> ranges;
334
335 SourceLocation startLoc;
336 SourceLocation endLoc;
337 bool noReturnType = isa<CXXConstructorDecl>(functionDecl)
338 || isa<CXXDestructorDecl>(functionDecl)
339 || isa<CXXConversionDecl>(functionDecl);
340 bool startAfterReturnType = !noReturnType;
341 if (startAfterReturnType)
342 {
343 // For functions that do have a return type, start searching for "SAL_CALL" after the return
344 // type (which for SAL_CALL functions on Windows will be an AttributedTypeLoc, which the
345 // implementation of FunctionDecl::getReturnTypeSourceRange does not take into account, so
346 // do that here explicitly):
347 auto const TSI = functionDecl->getTypeSourceInfo();
348 if (TSI == nullptr)
349 {
350 if (isDebugMode())
351 {
352 report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #1, needs investigation",
353 functionDecl->getLocation())
354 << functionDecl->getSourceRange();
355 }
356 return false;
357 }
358 auto TL = TSI->getTypeLoc().IgnoreParens();
359 if (auto ATL = TL.getAs<AttributedTypeLoc>())
360 {
361 TL = ATL.getModifiedLoc();
362 }
363 auto const FTL = TL.getAs<FunctionTypeLoc>();
364 if (!FTL)
365 {
366 // Happens when a function declaration uses a typedef for the function type, as in
367 //
368 // SAL_JNI_EXPORT javaunohelper::detail::Func_bootstrap
369 // Java_com_sun_star_comp_helper_Bootstrap_cppuhelper_1bootstrap;
370 //
371 // in javaunohelper/source/juhx-export-functions.hxx.
372 //TODO: check the typedef for mention of "SAL_CALL" (and also check for usage of such
373 // typedefs in the !startAfterReturnType case below)
374 return false;
375 }
376 startLoc = FTL.getReturnLoc().getEndLoc();
377 while (SM.isMacroArgExpansion(startLoc, &startLoc))
378 {
379 }
380
381 // Stop searching for "SAL_CALL" at the start of the function declaration's name (for
382 // qualified names this will point after the qualifiers, but needlessly including those in
383 // the search should be harmless---modulo issues with using "SAL_CALL" as the name of a
384 // function-like macro parameter as discussed below):
385 endLoc = compat::getBeginLoc(functionDecl->getNameInfo());
386 while (SM.isMacroArgExpansion(endLoc, &endLoc))
387 {
388 }
389 while (endLoc.isMacroID() && SM.isAtStartOfImmediateMacroExpansion(endLoc, &endLoc))
390 {
391 }
392 endLoc = SM.getSpellingLoc(endLoc);
393
394 auto const slEnd = Lexer::getLocForEndOfToken(startLoc, 0, SM, compiler.getLangOpts());
395 if (slEnd.isValid())
396 {
397 // startLoc is either non-macro, or at end of macro; one source range from startLoc to
398 // endLoc:
399 startLoc = slEnd;
400 while (startLoc.isMacroID() && SM.isAtEndOfImmediateMacroExpansion(startLoc, &startLoc))
401 {
402 }
403 startLoc = SM.getSpellingLoc(startLoc);
404
405 if (startLoc.isValid() && endLoc.isValid() && startLoc != endLoc
406 && !SM.isBeforeInTranslationUnit(startLoc, endLoc))
407 {
408 // Happens for uses of trailing return type (in which case starting instead at the
409 // start of the function declaration should be fine), but also for cases like
410 //
411 // void (*f())();
412 //
413 // where the function name is within the function type (TODO: in which case starting
414 // at the start can erroneously pick up the "SAL_CALL" from the returned pointer-to-
415 // function type in cases like
416 //
417 // void SAL_CALL (*f())();
418 //
419 // that are hopefully rare):
420 startAfterReturnType = false;
421 }
422 }
423 else
424 {
425 // startLoc is within a macro body; two source ranges, first is the remainder of the
426 // corresponding macro definition's replacement text, second is from after the macro
427 // invocation to endLoc, unless endLoc is already in the first range:
428 //TODO: If the macro is a function-like macro with a parameter named "SAL_CALL", uses of
429 // that parameter in the remainder of the replacement text will be false positives.
430 assert(SM.isMacroBodyExpansion(startLoc));
431 auto const startLoc2 = compat::getImmediateExpansionRange(SM, startLoc).second;
432 auto name = Lexer::getImmediateMacroName(startLoc, SM, compiler.getLangOpts());
433 while (name.startswith("\\\n"))
434 {
435 name = name.drop_front(2);
436 while (!name.empty()
437 && (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
438 || name.front() == '\v' || name.front() == '\f'))
439 {
440 name = name.drop_front(1);
441 }
442 }
443 auto const MI = compiler.getPreprocessor()
444 .getMacroDefinitionAtLoc(&compiler.getASTContext().Idents.get(name),
445 SM.getSpellingLoc(startLoc))
446 .getMacroInfo();
447 assert(MI != nullptr);
448 auto endLoc1 = MI->getDefinitionEndLoc();
449 assert(endLoc1.isFileID());
450 endLoc1 = Lexer::getLocForEndOfToken(endLoc1, 0, SM, compiler.getLangOpts());
451 startLoc = Lexer::getLocForEndOfToken(SM.getSpellingLoc(startLoc), 0, SM,
452 compiler.getLangOpts());
453 if (!compat::isPointWithin(SM, endLoc, startLoc, endLoc1))
454 {
455 ranges.emplace_back(startLoc, endLoc1);
456 startLoc = Lexer::getLocForEndOfToken(SM.getSpellingLoc(startLoc2), 0, SM,
457 compiler.getLangOpts());
458 }
459 }
460 }
461 if (!startAfterReturnType)
462 {
463 // Stop searching for "SAL_CALL" at the start of the function declaration's name (for
464 // qualified names this will point after the qualifiers, but needlessly including those in
465 // the search should be harmless):
466 endLoc = compat::getBeginLoc(functionDecl->getNameInfo());
467 while (endLoc.isMacroID() && SM.isAtStartOfImmediateMacroExpansion(endLoc, &endLoc))
468 {
469 }
470
471 SourceRange macroRange;
472 if (SM.isMacroBodyExpansion(endLoc))
473 {
474 auto name = Lexer::getImmediateMacroName(endLoc, SM, compiler.getLangOpts());
475 while (name.startswith("\\\n"))
476 {
477 name = name.drop_front(2);
478 while (!name.empty()
479 && (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
480 || name.front() == '\v' || name.front() == '\f'))
481 {
482 name = name.drop_front(1);
483 }
484 }
485 auto const MI = compiler.getPreprocessor()
486 .getMacroDefinitionAtLoc(&compiler.getASTContext().Idents.get(name),
487 SM.getSpellingLoc(endLoc))
488 .getMacroInfo();
489 assert(MI != nullptr);
490 macroRange = SourceRange(MI->getDefinitionLoc(), MI->getDefinitionEndLoc());
491 if (isDebugMode() && macroRange.isInvalid())
492 {
493 report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #4, needs investigation",
494 functionDecl->getLocation())
495 << functionDecl->getSourceRange();
496 }
497 }
498
499 #if defined _WIN32
500 auto const macroExpansion = SM.getExpansionLoc(endLoc);
501 #endif
502 endLoc = SM.getSpellingLoc(endLoc);
503
504 // Ctors/dtors/conversion functions don't have a return type, start searching for "SAL_CALL"
505 // at the start of the function declaration:
506 startLoc = functionDecl->getSourceRange().getBegin();
507 while (startLoc.isMacroID()
508 && !(macroRange.isValid()
509 && compat::isPointWithin(SM, SM.getSpellingLoc(startLoc), macroRange.getBegin(),
510 macroRange.getEnd()))
511 && SM.isAtStartOfImmediateMacroExpansion(startLoc, &startLoc))
512 {
513 }
514 #if !defined _WIN32
515 auto const macroStartLoc = startLoc;
516 #endif
517 startLoc = SM.getSpellingLoc(startLoc);
518
519 #if defined _WIN32
520 if (macroRange.isValid()
521 && !compat::isPointWithin(SM, startLoc, macroRange.getBegin(), macroRange.getEnd()))
522 {
523 // endLoc is within a macro body but startLoc is not; two source ranges, first is from
524 // startLoc to the macro invocation, second is the leading part of the corresponding
525 // macro definition's replacement text:
526 ranges.emplace_back(startLoc, macroExpansion);
527 startLoc = macroRange.getBegin();
528 }
529 #else
530 // When the SAL_CALL macro expands to nothing, it may even precede the function
531 // declaration's source range, so go back one token (unless the declaration is known to
532 // start with a token that must precede a possible "SAL_CALL", like "virtual" or
533 // "explicit"):
534 //TODO: this will produce false positives if the declaration is immediately preceded by a
535 // macro definition whose replacement text ends in "SAL_CALL"
536 if (noReturnType
537 && !(functionDecl->isVirtualAsWritten()
538 || (isa<CXXConstructorDecl>(functionDecl)
539 && compat::isExplicitSpecified(cast<CXXConstructorDecl>(functionDecl)))
540 || (isa<CXXConversionDecl>(functionDecl)
541 && compat::isExplicitSpecified(cast<CXXConversionDecl>(functionDecl)))))
542 {
543 SourceLocation endLoc1;
544 if (macroStartLoc.isMacroID()
545 && SM.isAtStartOfImmediateMacroExpansion(macroStartLoc, &endLoc1))
546 {
547 // startLoc is at the start of a macro body; two source ranges, first one is looking
548 // backwards one token from the call site of the macro:
549 auto startLoc1 = endLoc1;
550 for (;;)
551 {
552 startLoc1 = Lexer::GetBeginningOfToken(startLoc1.getLocWithOffset(-1), SM,
553 compiler.getLangOpts());
554 auto const s = StringRef(
555 SM.getCharacterData(startLoc1),
556 Lexer::MeasureTokenLength(startLoc1, SM, compiler.getLangOpts()));
557 // When looking backward at least through a function-like macro replacement like
558 //
559 // | foo\ |
560 // | barbaz##X |
561 //
562 // starting at "barbaz" in the second line, the next token reported will start at "\"
563 // in the first line and include the intervening spaces and (part of? looks like an
564 // error in Clang) "barbaz", so just skip any tokens starting with backslash-newline
565 // when looking backwards here, without even trying to look at their content:
566 if (!(s.empty() || s.startswith("/*") || s.startswith("//")
567 || s.startswith("\\\n")))
568 {
569 break;
570 }
571 }
572 ranges.emplace_back(startLoc1, endLoc1);
573 }
574 else
575 {
576 for (;;)
577 {
578 startLoc = Lexer::GetBeginningOfToken(startLoc.getLocWithOffset(-1), SM,
579 compiler.getLangOpts());
580 auto const s = StringRef(
581 SM.getCharacterData(startLoc),
582 Lexer::MeasureTokenLength(startLoc, SM, compiler.getLangOpts()));
583 // When looking backward at least through a function-like macro replacement like
584 //
585 // | foo\ |
586 // | barbaz##X |
587 //
588 // starting at "barbaz" in the second line, the next token reported will start at "\"
589 // in the first line and include the intervening spaces and (part of? looks like an
590 // error in Clang) "barbaz", so just skip any tokens starting with backslash-newline
591 // when looking backwards here, without even trying to look at their content:
592 if (!(s.empty() || s.startswith("/*") || s.startswith("//")
593 || s.startswith("\\\n")))
594 {
595 break;
596 }
597 }
598 }
599 }
600 #endif
601 }
602 ranges.emplace_back(startLoc, endLoc);
603
604 for (auto const range : ranges)
605 {
606 if (range.isInvalid())
607 {
608 if (isDebugMode())
609 {
610 report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #2, needs investigation",
611 functionDecl->getLocation())
612 << functionDecl->getSourceRange();
613 }
614 return false;
615 }
616 if (isDebugMode() && range.getBegin() != range.getEnd()
617 && !SM.isBeforeInTranslationUnit(range.getBegin(), range.getEnd()))
618 {
619 report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #3, needs investigation",
620 functionDecl->getLocation())
621 << functionDecl->getSourceRange();
622 }
623
624 for (auto loc = range.getBegin(); SM.isBeforeInTranslationUnit(loc, range.getEnd());)
625 {
626 unsigned n = Lexer::MeasureTokenLength(loc, SM, compiler.getLangOpts());
627 auto s = StringRef(compiler.getSourceManager().getCharacterData(loc), n);
628 while (s.startswith("\\\n"))
629 {
630 s = s.drop_front(2);
631 while (!s.empty()
632 && (s.front() == ' ' || s.front() == '\t' || s.front() == '\n'
633 || s.front() == '\v' || s.front() == '\f'))
634 {
635 s = s.drop_front(1);
636 }
637 }
638 if (s == "SAL_CALL")
639 {
640 if (pLoc)
641 *pLoc = loc;
642 return true;
643 }
644 loc = loc.getLocWithOffset(std::max<unsigned>(n, 1));
645 }
646 }
647 return false;
648 }
649
rewrite(SourceLocation locBegin)650 bool SalCall::rewrite(SourceLocation locBegin)
651 {
652 if (!rewriter)
653 return false;
654 if (!locBegin.isValid())
655 return false;
656
657 auto locEnd = locBegin.getLocWithOffset(8);
658 if (!locEnd.isValid())
659 return false;
660
661 SourceRange range(locBegin, locEnd);
662
663 if (!replaceText(locBegin, 9, ""))
664 return false;
665
666 return true;
667 }
668
669 static loplugin::Plugin::Registration<SalCall> reg("salcall", true);
670 }
671
672 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
673