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