/* -*- 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/.
*/
// 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.)
// 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 elseif (bDeclIsSalCall)
{ // not fine
report(DiagnosticsEngine::Warning, "SAL_CALL inconsistency", decl->getLocation())
<< decl->getSourceRange();
report(DiagnosticsEngine::Note, "SAL_CALL inconsistency", canonicalDecl->getLocation())
<< canonicalDecl->getSourceRange(); returntrue;
}
} 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(); returntrue;
}
}
}
if (!bCanonicalDeclIsSalCall) returntrue;
if (!decl->isThisDeclarationADefinition() && !(methodDecl && compat::isPureVirtual(methodDecl))) returntrue;
m_decls.insert(decl); returntrue;
}
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("accessibility").GlobalNamespace()
|| dc.Class("OComponentProxyAggregationHelper")
.Namespace("accessibility")
.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("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;
}
}
//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)
{ returnfalse;
}
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): autoconst TSI = functionDecl->getTypeSourceInfo(); if (TSI == nullptr)
{ if (isDebugMode())
{
report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #1, needs investigation",
functionDecl->getLocation())
<< functionDecl->getSourceRange();
} returnfalse;
} auto TL = TSI->getTypeLoc().IgnoreParens(); if (auto ATL = TL.getAs<AttributedTypeLoc>())
{
TL = ATL.getModifiedLoc();
} autoconst 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) returnfalse;
}
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);
autoconst 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)); autoconst startLoc2 = compat::getImmediateExpansionRange(SM, startLoc).second; auto name = Lexer::getImmediateMacroName(startLoc, SM, compiler.getLangOpts()); while (compat::starts_with(name, "\\\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);
}
} autoconst 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 (compat::starts_with(name, "\\\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);
}
} autoconst 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();
}
}
// 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 autoconst macroStartLoc = startLoc; #endif
startLoc = SM.getSpellingLoc(startLoc);
#ifdefined _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()); autoconst 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() || compat::starts_with(s, "/*") || compat::starts_with(s, "//")
|| compat::starts_with(s, "\\\n")))
{ break;
}
}
ranges.emplace_back(startLoc1, endLoc1);
} else
{ for (;;)
{
startLoc = Lexer::GetBeginningOfToken(startLoc.getLocWithOffset(-1), SM,
compiler.getLangOpts()); autoconst 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() || compat::starts_with(s, "/*") || compat::starts_with(s, "//")
|| compat::starts_with(s, "\\\n")))
{ break;
}
}
}
} #endif
}
ranges.emplace_back(startLoc, endLoc);
for (autoconst& range : ranges)
{ if (range.isInvalid())
{ if (isDebugMode())
{
report(DiagnosticsEngine::Fatal, "TODO: unexpected failure #2, needs investigation",
functionDecl->getLocation())
<< functionDecl->getSourceRange();
} returnfalse;
} 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 (compat::starts_with(s, "\\\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; returntrue;
}
loc = loc.getLocWithOffset(std::max<unsigned>(n, 1));
}
} returnfalse;
}
bool SalCall::rewrite(SourceLocation locBegin)
{ if (!rewriter) returnfalse; if (!locBegin.isValid()) returnfalse;
auto locEnd = locBegin.getLocWithOffset(8); if (!locEnd.isValid()) returnfalse;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.