d1a2b80b9d
...as discussed in the mail thread starting at <https://lists.freedesktop.org/archives/libreoffice/2020-November/086234.html> "Bump --enable-compiler-plugins Clang baseline?" (and now picked up again at <https://lists.freedesktop.org/archives/libreoffice/2022-February/088459.html> "Re: Bump --enable-compiler-plugins Clang baseline?"), and clean up compilerplugins/clang/ accordingly Change-Id: I5e81c6fdcc363aeefd6227606225b526fdf7ac16 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129989 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
615 lines
26 KiB
C++
615 lines
26 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "plugin.hxx"
|
|
#include "check.hxx"
|
|
#include "compat.hxx"
|
|
#include "functionaddress.hxx"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
// The SAL_CALL function annotation is only necessary on our outward
|
|
// facing C++ ABI, anywhere else it is just cargo-cult.
|
|
//
|
|
|
|
//TODO: To find inconsistencies like
|
|
//
|
|
// template<typename> struct S { void f(); }; // #1
|
|
// template<typename T> void S<T>::f() {} // #2
|
|
// template void SAL_CALL S<void>::f();
|
|
//
|
|
// VisitFunctionDecl would need to also visit explicit instantiations, by letting
|
|
// shouldVisitTemplateInstantiations return true and returning from VisitFunctionDecl early iff
|
|
// decl->getTemplateSpecializationKind() == TSK_ImplicitInstantiation. However, an instantiated
|
|
// FunctionDecl is created in TemplateDeclInstantiator::VisitCXXMethodDecl by copying information
|
|
// (including source locations) from the declaration at #1, and later modified in
|
|
// Sema::InstantiateFunctionDefinition with some source location information from the definition at
|
|
// #2. That means that the source scanning in isSalCallFunction below would be thoroughly confused
|
|
// and break. (This happens for both explicit and implicit template instantiations, which is the
|
|
// reason why calls to isSalCallFunction make sure to not call it with any FunctionDecls
|
|
// representing such template instantiations.)
|
|
|
|
namespace
|
|
{
|
|
//static bool startswith(const std::string& rStr, const char* pSubStr)
|
|
//{
|
|
// return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
|
|
//}
|
|
|
|
CXXMethodDecl const* getTemplateInstantiationPattern(CXXMethodDecl const* decl)
|
|
{
|
|
auto const p = decl->getTemplateInstantiationPattern();
|
|
return p == nullptr ? decl : cast<CXXMethodDecl>(p);
|
|
}
|
|
|
|
class SalCall final : public loplugin::FunctionAddress<loplugin::FilteringRewritePlugin<SalCall>>
|
|
{
|
|
public:
|
|
explicit SalCall(loplugin::InstantiationData const& data)
|
|
: FunctionAddress(data)
|
|
{
|
|
}
|
|
|
|
virtual void run() override
|
|
{
|
|
if (TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()))
|
|
{
|
|
auto const& addressOfSet = getFunctionsWithAddressTaken();
|
|
for (auto const decl : m_decls)
|
|
{
|
|
if (addressOfSet.find(decl->getCanonicalDecl()) == addressOfSet.end())
|
|
{
|
|
handleFunctionDecl(decl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool VisitFunctionDecl(FunctionDecl const*);
|
|
|
|
private:
|
|
void handleFunctionDecl(FunctionDecl const* decl);
|
|
bool rewrite(SourceLocation);
|
|
bool isSalCallFunction(FunctionDecl const* functionDecl, SourceLocation* pLoc = nullptr);
|
|
|
|
std::set<FunctionDecl const*> m_decls;
|
|
};
|
|
|
|
bool SalCall::VisitFunctionDecl(FunctionDecl const* decl)
|
|
{
|
|
if (ignoreLocation(decl))
|
|
return true;
|
|
|
|
// ignore template stuff
|
|
if (decl->getTemplatedKind() != clang::FunctionDecl::TK_NonTemplate)
|
|
return true;
|
|
auto recordDecl = dyn_cast<CXXRecordDecl>(decl->getDeclContext());
|
|
if (recordDecl
|
|
&& (recordDecl->getTemplateSpecializationKind() != TSK_Undeclared
|
|
|| recordDecl->isDependentContext()))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
auto canonicalDecl = decl->getCanonicalDecl();
|
|
|
|
// ignore UNO implementations
|
|
if (isInUnoIncludeFile(
|
|
compiler.getSourceManager().getSpellingLoc(canonicalDecl->getLocation())))
|
|
return true;
|
|
|
|
SourceLocation rewriteLoc;
|
|
SourceLocation rewriteCanonicalLoc;
|
|
bool bDeclIsSalCall = isSalCallFunction(decl, &rewriteLoc);
|
|
bool bCanonicalDeclIsSalCall = isSalCallFunction(canonicalDecl, &rewriteCanonicalLoc);
|
|
|
|
// first, check for consistency, so we don't trip ourselves up on Linux, where we normally run the plugin
|
|
if (canonicalDecl != decl)
|
|
{
|
|
if (bCanonicalDeclIsSalCall)
|
|
; // this is fine, the actual definition have or not have SAL_CALL, and MSVC is fine with it
|
|
else if (bDeclIsSalCall)
|
|
{
|
|
// not fine
|
|
report(DiagnosticsEngine::Warning, "SAL_CALL inconsistency", decl->getLocation())
|
|
<< decl->getSourceRange();
|
|
report(DiagnosticsEngine::Note, "SAL_CALL inconsistency", canonicalDecl->getLocation())
|
|
<< canonicalDecl->getSourceRange();
|
|
return true;
|
|
}
|
|
}
|
|
auto methodDecl = dyn_cast<CXXMethodDecl>(canonicalDecl);
|
|
if (methodDecl)
|
|
{
|
|
for (auto iter = methodDecl->begin_overridden_methods();
|
|
iter != methodDecl->end_overridden_methods(); ++iter)
|
|
{
|
|
const CXXMethodDecl* overriddenMethod
|
|
= getTemplateInstantiationPattern(*iter)->getCanonicalDecl();
|
|
if (bCanonicalDeclIsSalCall != isSalCallFunction(overriddenMethod))
|
|
{
|
|
report(DiagnosticsEngine::Warning, "SAL_CALL inconsistency",
|
|
methodDecl->getLocation())
|
|
<< methodDecl->getSourceRange();
|
|
report(DiagnosticsEngine::Note, "SAL_CALL inconsistency",
|
|
overriddenMethod->getLocation())
|
|
<< overriddenMethod->getSourceRange();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bCanonicalDeclIsSalCall)
|
|
return true;
|
|
|
|
if (!decl->isThisDeclarationADefinition() && !(methodDecl && methodDecl->isPure()))
|
|
return true;
|
|
|
|
m_decls.insert(decl);
|
|
return true;
|
|
}
|
|
|
|
void SalCall::handleFunctionDecl(FunctionDecl const* decl)
|
|
{
|
|
// some base classes are overridden by sub-classes which override both the base-class and a UNO class
|
|
if (auto recordDecl = dyn_cast<CXXRecordDecl>(decl->getDeclContext()))
|
|
{
|
|
auto dc = loplugin::DeclCheck(recordDecl);
|
|
if (dc.Class("OProxyAggregation").Namespace("comphelper").GlobalNamespace()
|
|
|| dc.Class("OComponentProxyAggregationHelper")
|
|
.Namespace("comphelper")
|
|
.GlobalNamespace()
|
|
|| dc.Class("SvxShapeMaster").GlobalNamespace()
|
|
|| dc.Class("ListBoxAccessibleBase").Namespace("accessibility").GlobalNamespace()
|
|
|| dc.Class("AsyncEventNotifierBase").Namespace("comphelper").GlobalNamespace()
|
|
|| dc.Class("ODescriptor")
|
|
.Namespace("sdbcx")
|
|
.Namespace("connectivity")
|
|
.GlobalNamespace()
|
|
|| dc.Class("IController").Namespace("dbaui").GlobalNamespace()
|
|
|| dc.Class("ORowSetBase").Namespace("dbaccess").GlobalNamespace()
|
|
|| dc.Class("OComponentAdapterBase").Namespace("bib").GlobalNamespace()
|
|
|| dc.Class("IEventProcessor").Namespace("comphelper").GlobalNamespace()
|
|
|| dc.Class("SvxUnoTextBase").GlobalNamespace()
|
|
|| dc.Class("OInterfaceContainer").Namespace("frm").GlobalNamespace()
|
|
|| dc.Class("AccessibleComponentBase").Namespace("accessibility").GlobalNamespace()
|
|
|| dc.Class("ContextHandler2Helper")
|
|
.Namespace("core")
|
|
.Namespace("oox")
|
|
.GlobalNamespace()
|
|
|| dc.Class("AccessibleStaticTextBase").Namespace("accessibility").GlobalNamespace()
|
|
|| dc.Class("OCommonPicker").Namespace("svt").GlobalNamespace()
|
|
|| dc.Class("VbaDocumentBase").GlobalNamespace()
|
|
|| dc.Class("VbaPageSetupBase").GlobalNamespace()
|
|
|| dc.Class("ScVbaControl").GlobalNamespace()
|
|
|
|
)
|
|
return;
|
|
}
|
|
|
|
auto canonicalDecl = decl->getCanonicalDecl();
|
|
|
|
// if any of the overridden methods are SAL_CALL, we should be too
|
|
if (auto methodDecl = dyn_cast<CXXMethodDecl>(canonicalDecl))
|
|
{
|
|
for (auto iter = methodDecl->begin_overridden_methods();
|
|
iter != methodDecl->end_overridden_methods(); ++iter)
|
|
{
|
|
const CXXMethodDecl* overriddenMethod
|
|
= getTemplateInstantiationPattern(*iter)->getCanonicalDecl();
|
|
if (isSalCallFunction(overriddenMethod))
|
|
return;
|
|
}
|
|
}
|
|
|
|
SourceLocation rewriteLoc;
|
|
SourceLocation rewriteCanonicalLoc;
|
|
bool bDeclIsSalCall = isSalCallFunction(decl, &rewriteLoc);
|
|
isSalCallFunction(canonicalDecl, &rewriteCanonicalLoc);
|
|
|
|
bool bOK = rewrite(rewriteLoc);
|
|
if (bOK && canonicalDecl != decl)
|
|
{
|
|
bOK = rewrite(rewriteCanonicalLoc);
|
|
}
|
|
if (bOK)
|
|
return;
|
|
|
|
if (bDeclIsSalCall)
|
|
{
|
|
report(DiagnosticsEngine::Warning, "SAL_CALL unnecessary here",
|
|
rewriteLoc.isValid() ? rewriteLoc : decl->getLocation())
|
|
<< decl->getSourceRange();
|
|
}
|
|
if (canonicalDecl != decl)
|
|
{
|
|
report(DiagnosticsEngine::Warning, "SAL_CALL unnecessary here", rewriteCanonicalLoc)
|
|
<< canonicalDecl->getSourceRange();
|
|
if (!bDeclIsSalCall)
|
|
{
|
|
report(DiagnosticsEngine::Note, "defined here (without SAL_CALL decoration)",
|
|
decl->getLocation())
|
|
<< decl->getSourceRange();
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO: This doesn't handle all possible cases of macro usage (and possibly never will be able to),
|
|
// just what is encountered in practice:
|
|
bool SalCall::isSalCallFunction(FunctionDecl const* functionDecl, SourceLocation* pLoc)
|
|
{
|
|
assert(!functionDecl->isTemplateInstantiation());
|
|
|
|
//TODO: It appears that FunctionDecls representing explicit template specializations have the
|
|
// same issue as those representing (implicit or explicit) instantiations, namely that their
|
|
// data (including relevant source locations) is an incoherent combination of data from the
|
|
// original template declaration and the later specialization definition. For example, for the
|
|
// OValueLimitedType<double>::registerProperties specialization at
|
|
// forms/source/xforms/datatyperepository.cxx:241, the FunctionDecl (which is even considered
|
|
// canonic) representing the base-class function overridden by ODecimalType::registerProperties
|
|
// (forms/source/xforms/datatypes.hxx:299) is dumped as
|
|
//
|
|
// CXXMethodDecl <forms/source/xforms/datatypes.hxx:217:9, col:54>
|
|
// forms/source/xforms/datatyperepository.cxx:242:37 registerProperties 'void (void)' virtual
|
|
//
|
|
// mixing the source range ("datatypes.hxx:217:9, col:54") from the original declaration with
|
|
// the name location ("datatyperepository.cxx:242:37") from the explicit specialization. Just
|
|
// give up for now and assume no "SAL_CALL" is present:
|
|
if (functionDecl->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SourceManager& SM = compiler.getSourceManager();
|
|
std::vector<SourceRange> ranges;
|
|
|
|
SourceLocation startLoc;
|
|
SourceLocation endLoc;
|
|
bool noReturnType = isa<CXXConstructorDecl>(functionDecl)
|
|
|| isa<CXXDestructorDecl>(functionDecl)
|
|
|| isa<CXXConversionDecl>(functionDecl);
|
|
bool startAfterReturnType = !noReturnType;
|
|
if (startAfterReturnType)
|
|
{
|
|
// For functions that do have a return type, start searching for "SAL_CALL" after the return
|
|
// type (which for SAL_CALL functions on Windows will be an AttributedTypeLoc, which the
|
|
// implementation of FunctionDecl::getReturnTypeSourceRange does not take into account, so
|
|
// do that here explicitly):
|
|
auto const TSI = functionDecl->getTypeSourceInfo();
|
|
if (TSI == nullptr)
|
|
{
|
|
if (isDebugMode())
|
|
{
|
|
report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #1, needs investigation",
|
|
functionDecl->getLocation())
|
|
<< functionDecl->getSourceRange();
|
|
}
|
|
return false;
|
|
}
|
|
auto TL = TSI->getTypeLoc().IgnoreParens();
|
|
if (auto ATL = TL.getAs<AttributedTypeLoc>())
|
|
{
|
|
TL = ATL.getModifiedLoc();
|
|
}
|
|
auto const FTL = TL.getAs<FunctionTypeLoc>();
|
|
if (!FTL)
|
|
{
|
|
// Happens when a function declaration uses a typedef for the function type, as in
|
|
//
|
|
// SAL_JNI_EXPORT javaunohelper::detail::Func_bootstrap
|
|
// Java_com_sun_star_comp_helper_Bootstrap_cppuhelper_1bootstrap;
|
|
//
|
|
// in javaunohelper/source/juhx-export-functions.hxx.
|
|
//TODO: check the typedef for mention of "SAL_CALL" (and also check for usage of such
|
|
// typedefs in the !startAfterReturnType case below)
|
|
return false;
|
|
}
|
|
startLoc = FTL.getReturnLoc().getEndLoc();
|
|
while (SM.isMacroArgExpansion(startLoc, &startLoc))
|
|
{
|
|
}
|
|
|
|
// Stop searching for "SAL_CALL" at the start of the function declaration's name (for
|
|
// qualified names this will point after the qualifiers, but needlessly including those in
|
|
// the search should be harmless---modulo issues with using "SAL_CALL" as the name of a
|
|
// function-like macro parameter as discussed below):
|
|
endLoc = functionDecl->getNameInfo().getBeginLoc();
|
|
while (SM.isMacroArgExpansion(endLoc, &endLoc))
|
|
{
|
|
}
|
|
while (endLoc.isMacroID() && SM.isAtStartOfImmediateMacroExpansion(endLoc, &endLoc))
|
|
{
|
|
}
|
|
endLoc = SM.getSpellingLoc(endLoc);
|
|
|
|
auto const slEnd = Lexer::getLocForEndOfToken(startLoc, 0, SM, compiler.getLangOpts());
|
|
if (slEnd.isValid())
|
|
{
|
|
// startLoc is either non-macro, or at end of macro; one source range from startLoc to
|
|
// endLoc:
|
|
startLoc = slEnd;
|
|
while (startLoc.isMacroID() && SM.isAtEndOfImmediateMacroExpansion(startLoc, &startLoc))
|
|
{
|
|
}
|
|
startLoc = SM.getSpellingLoc(startLoc);
|
|
|
|
if (startLoc.isValid() && endLoc.isValid() && startLoc != endLoc
|
|
&& !SM.isBeforeInTranslationUnit(startLoc, endLoc))
|
|
{
|
|
// Happens for uses of trailing return type (in which case starting instead at the
|
|
// start of the function declaration should be fine), but also for cases like
|
|
//
|
|
// void (*f())();
|
|
//
|
|
// where the function name is within the function type (TODO: in which case starting
|
|
// at the start can erroneously pick up the "SAL_CALL" from the returned pointer-to-
|
|
// function type in cases like
|
|
//
|
|
// void SAL_CALL (*f())();
|
|
//
|
|
// that are hopefully rare):
|
|
startAfterReturnType = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// startLoc is within a macro body; two source ranges, first is the remainder of the
|
|
// corresponding macro definition's replacement text, second is from after the macro
|
|
// invocation to endLoc, unless endLoc is already in the first range:
|
|
//TODO: If the macro is a function-like macro with a parameter named "SAL_CALL", uses of
|
|
// that parameter in the remainder of the replacement text will be false positives.
|
|
assert(SM.isMacroBodyExpansion(startLoc));
|
|
auto const startLoc2 = compat::getImmediateExpansionRange(SM, startLoc).second;
|
|
auto name = Lexer::getImmediateMacroName(startLoc, SM, compiler.getLangOpts());
|
|
while (name.startswith("\\\n"))
|
|
{
|
|
name = name.drop_front(2);
|
|
while (!name.empty()
|
|
&& (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
|
|
|| name.front() == '\v' || name.front() == '\f'))
|
|
{
|
|
name = name.drop_front(1);
|
|
}
|
|
}
|
|
auto const MI = compiler.getPreprocessor()
|
|
.getMacroDefinitionAtLoc(&compiler.getASTContext().Idents.get(name),
|
|
SM.getSpellingLoc(startLoc))
|
|
.getMacroInfo();
|
|
assert(MI != nullptr);
|
|
auto endLoc1 = MI->getDefinitionEndLoc();
|
|
assert(endLoc1.isFileID());
|
|
endLoc1 = Lexer::getLocForEndOfToken(endLoc1, 0, SM, compiler.getLangOpts());
|
|
startLoc = Lexer::getLocForEndOfToken(SM.getSpellingLoc(startLoc), 0, SM,
|
|
compiler.getLangOpts());
|
|
if (!SM.isPointWithin(endLoc, startLoc, endLoc1))
|
|
{
|
|
ranges.emplace_back(startLoc, endLoc1);
|
|
startLoc = Lexer::getLocForEndOfToken(SM.getSpellingLoc(startLoc2), 0, SM,
|
|
compiler.getLangOpts());
|
|
}
|
|
}
|
|
}
|
|
if (!startAfterReturnType)
|
|
{
|
|
// Stop searching for "SAL_CALL" at the start of the function declaration's name (for
|
|
// qualified names this will point after the qualifiers, but needlessly including those in
|
|
// the search should be harmless):
|
|
endLoc = functionDecl->getNameInfo().getBeginLoc();
|
|
while (endLoc.isMacroID() && SM.isAtStartOfImmediateMacroExpansion(endLoc, &endLoc))
|
|
{
|
|
}
|
|
|
|
SourceRange macroRange;
|
|
if (SM.isMacroBodyExpansion(endLoc))
|
|
{
|
|
auto name = Lexer::getImmediateMacroName(endLoc, SM, compiler.getLangOpts());
|
|
while (name.startswith("\\\n"))
|
|
{
|
|
name = name.drop_front(2);
|
|
while (!name.empty()
|
|
&& (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
|
|
|| name.front() == '\v' || name.front() == '\f'))
|
|
{
|
|
name = name.drop_front(1);
|
|
}
|
|
}
|
|
auto const MI = compiler.getPreprocessor()
|
|
.getMacroDefinitionAtLoc(&compiler.getASTContext().Idents.get(name),
|
|
SM.getSpellingLoc(endLoc))
|
|
.getMacroInfo();
|
|
assert(MI != nullptr);
|
|
macroRange = SourceRange(MI->getDefinitionLoc(), MI->getDefinitionEndLoc());
|
|
if (isDebugMode() && macroRange.isInvalid())
|
|
{
|
|
report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #4, needs investigation",
|
|
functionDecl->getLocation())
|
|
<< functionDecl->getSourceRange();
|
|
}
|
|
}
|
|
|
|
#if defined _WIN32
|
|
auto const macroExpansion = SM.getExpansionLoc(endLoc);
|
|
#endif
|
|
endLoc = SM.getSpellingLoc(endLoc);
|
|
|
|
// Ctors/dtors/conversion functions don't have a return type, start searching for "SAL_CALL"
|
|
// at the start of the function declaration:
|
|
startLoc = functionDecl->getSourceRange().getBegin();
|
|
while (startLoc.isMacroID()
|
|
&& !(macroRange.isValid()
|
|
&& SM.isPointWithin(SM.getSpellingLoc(startLoc), macroRange.getBegin(),
|
|
macroRange.getEnd()))
|
|
&& SM.isAtStartOfImmediateMacroExpansion(startLoc, &startLoc))
|
|
{
|
|
}
|
|
#if !defined _WIN32
|
|
auto const macroStartLoc = startLoc;
|
|
#endif
|
|
startLoc = SM.getSpellingLoc(startLoc);
|
|
|
|
#if defined _WIN32
|
|
if (macroRange.isValid()
|
|
&& !SM.isPointWithin(startLoc, macroRange.getBegin(), macroRange.getEnd()))
|
|
{
|
|
// endLoc is within a macro body but startLoc is not; two source ranges, first is from
|
|
// startLoc to the macro invocation, second is the leading part of the corresponding
|
|
// macro definition's replacement text:
|
|
ranges.emplace_back(startLoc, macroExpansion);
|
|
startLoc = macroRange.getBegin();
|
|
}
|
|
#else
|
|
// When the SAL_CALL macro expands to nothing, it may even precede the function
|
|
// declaration's source range, so go back one token (unless the declaration is known to
|
|
// start with a token that must precede a possible "SAL_CALL", like "virtual" or
|
|
// "explicit"):
|
|
//TODO: this will produce false positives if the declaration is immediately preceded by a
|
|
// macro definition whose replacement text ends in "SAL_CALL"
|
|
if (noReturnType
|
|
&& !(functionDecl->isVirtualAsWritten()
|
|
|| (isa<CXXConstructorDecl>(functionDecl)
|
|
&& cast<CXXConstructorDecl>(functionDecl)->getExplicitSpecifier().isExplicit())
|
|
|| (isa<CXXConversionDecl>(functionDecl)
|
|
&& cast<CXXConversionDecl>(functionDecl)
|
|
->getExplicitSpecifier()
|
|
.isExplicit())))
|
|
{
|
|
SourceLocation endLoc1;
|
|
if (macroStartLoc.isMacroID()
|
|
&& SM.isAtStartOfImmediateMacroExpansion(macroStartLoc, &endLoc1))
|
|
{
|
|
// startLoc is at the start of a macro body; two source ranges, first one is looking
|
|
// backwards one token from the call site of the macro:
|
|
auto startLoc1 = endLoc1;
|
|
for (;;)
|
|
{
|
|
startLoc1 = Lexer::GetBeginningOfToken(startLoc1.getLocWithOffset(-1), SM,
|
|
compiler.getLangOpts());
|
|
auto const s = StringRef(
|
|
SM.getCharacterData(startLoc1),
|
|
Lexer::MeasureTokenLength(startLoc1, SM, compiler.getLangOpts()));
|
|
// When looking backward at least through a function-like macro replacement like
|
|
//
|
|
// | foo\ |
|
|
// | barbaz##X |
|
|
//
|
|
// starting at "barbaz" in the second line, the next token reported will start at "\"
|
|
// in the first line and include the intervening spaces and (part of? looks like an
|
|
// error in Clang) "barbaz", so just skip any tokens starting with backslash-newline
|
|
// when looking backwards here, without even trying to look at their content:
|
|
if (!(s.empty() || s.startswith("/*") || s.startswith("//")
|
|
|| s.startswith("\\\n")))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
ranges.emplace_back(startLoc1, endLoc1);
|
|
}
|
|
else
|
|
{
|
|
for (;;)
|
|
{
|
|
startLoc = Lexer::GetBeginningOfToken(startLoc.getLocWithOffset(-1), SM,
|
|
compiler.getLangOpts());
|
|
auto const s = StringRef(
|
|
SM.getCharacterData(startLoc),
|
|
Lexer::MeasureTokenLength(startLoc, SM, compiler.getLangOpts()));
|
|
// When looking backward at least through a function-like macro replacement like
|
|
//
|
|
// | foo\ |
|
|
// | barbaz##X |
|
|
//
|
|
// starting at "barbaz" in the second line, the next token reported will start at "\"
|
|
// in the first line and include the intervening spaces and (part of? looks like an
|
|
// error in Clang) "barbaz", so just skip any tokens starting with backslash-newline
|
|
// when looking backwards here, without even trying to look at their content:
|
|
if (!(s.empty() || s.startswith("/*") || s.startswith("//")
|
|
|| s.startswith("\\\n")))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
ranges.emplace_back(startLoc, endLoc);
|
|
|
|
for (auto const& range : ranges)
|
|
{
|
|
if (range.isInvalid())
|
|
{
|
|
if (isDebugMode())
|
|
{
|
|
report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #2, needs investigation",
|
|
functionDecl->getLocation())
|
|
<< functionDecl->getSourceRange();
|
|
}
|
|
return false;
|
|
}
|
|
if (isDebugMode() && range.getBegin() != range.getEnd()
|
|
&& !SM.isBeforeInTranslationUnit(range.getBegin(), range.getEnd()))
|
|
{
|
|
report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #3, needs investigation",
|
|
functionDecl->getLocation())
|
|
<< functionDecl->getSourceRange();
|
|
}
|
|
|
|
for (auto loc = range.getBegin(); SM.isBeforeInTranslationUnit(loc, range.getEnd());)
|
|
{
|
|
unsigned n = Lexer::MeasureTokenLength(loc, SM, compiler.getLangOpts());
|
|
auto s = StringRef(compiler.getSourceManager().getCharacterData(loc), n);
|
|
while (s.startswith("\\\n"))
|
|
{
|
|
s = s.drop_front(2);
|
|
while (!s.empty()
|
|
&& (s.front() == ' ' || s.front() == '\t' || s.front() == '\n'
|
|
|| s.front() == '\v' || s.front() == '\f'))
|
|
{
|
|
s = s.drop_front(1);
|
|
}
|
|
}
|
|
if (s == "SAL_CALL")
|
|
{
|
|
if (pLoc)
|
|
*pLoc = loc;
|
|
return true;
|
|
}
|
|
loc = loc.getLocWithOffset(std::max<unsigned>(n, 1));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SalCall::rewrite(SourceLocation locBegin)
|
|
{
|
|
if (!rewriter)
|
|
return false;
|
|
if (!locBegin.isValid())
|
|
return false;
|
|
|
|
auto locEnd = locBegin.getLocWithOffset(8);
|
|
if (!locEnd.isValid())
|
|
return false;
|
|
|
|
SourceRange range(locBegin, locEnd);
|
|
|
|
if (!replaceText(locBegin, 9, ""))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static loplugin::Plugin::Registration<SalCall> reg("salcall", true);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|