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