Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

871 lines
29 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* Based on LLVM/Clang.
*
* This file is distributed under the University of Illinois Open Source
* License. See LICENSE.TXT for details.
*
*/
#include "plugin.hxx"
#include <cassert>
#include <cstddef>
#include <string>
#include <clang/Basic/FileManager.h>
#include <clang/Lex/Lexer.h>
#include "config_clang.h"
#include "compat.hxx"
#include "pluginhandler.hxx"
#include "check.hxx"
#if CLANG_VERSION >= 110000
#include "clang/AST/ParentMapContext.h"
#endif
/*
Base classes for plugin actions.
*/
namespace loplugin
{
namespace {
Expr const * skipImplicit(Expr const * expr) {
if (auto const e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
expr = compat::getSubExpr(e)->IgnoreImpCasts();
}
if (auto const e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
expr = e->getSubExpr();
}
return expr;
}
bool structurallyIdentical(Stmt const * stmt1, Stmt const * stmt2) {
if (stmt1->getStmtClass() != stmt2->getStmtClass()) {
return false;
}
switch (stmt1->getStmtClass()) {
case Stmt::CXXConstructExprClass:
if (cast<CXXConstructExpr>(stmt1)->getConstructor()->getCanonicalDecl()
!= cast<CXXConstructExpr>(stmt2)->getConstructor()->getCanonicalDecl())
{
return false;
}
break;
case Stmt::DeclRefExprClass:
if (cast<DeclRefExpr>(stmt1)->getDecl()->getCanonicalDecl()
!= cast<DeclRefExpr>(stmt2)->getDecl()->getCanonicalDecl())
{
return false;
}
break;
case Stmt::ImplicitCastExprClass:
{
auto const e1 = cast<ImplicitCastExpr>(stmt1);
auto const e2 = cast<ImplicitCastExpr>(stmt2);
if (e1->getCastKind() != e2->getCastKind()
|| e1->getType().getCanonicalType() != e2->getType().getCanonicalType())
{
return false;
}
break;
}
case Stmt::MemberExprClass:
{
auto const e1 = cast<MemberExpr>(stmt1);
auto const e2 = cast<MemberExpr>(stmt2);
if (e1->isArrow() != e2->isArrow()
|| e1->getType().getCanonicalType() != e2->getType().getCanonicalType())
{
return false;
}
break;
}
case Stmt::CXXMemberCallExprClass:
case Stmt::CXXOperatorCallExprClass:
if (cast<Expr>(stmt1)->getType().getCanonicalType()
!= cast<Expr>(stmt2)->getType().getCanonicalType())
{
return false;
}
break;
case Stmt::MaterializeTemporaryExprClass:
Adapt structurallyIdentical to current needs ...since aad94d48b19135a2e46ac7b2c0f41f3bb29bb5a7 "Silence warnings in salvtables.hxx", where --enable-compiler-plugins=debug had started to fail with > In file included from vcl/source/app/salvtables.cxx:75: > vcl/inc/salvtables.hxx:128:69: fatal error: TODO: Unexpected 'IdenticalDefaultArgumentsResult::Maybe' [loplugin:overrideparam] > weld_drawing_area(const OString& id, const a11yref& rA11yImpl = nullptr, > ^~~~~~~ > include/vcl/weld.hxx:2138:69: note: TODO: second argument is here [loplugin:overrideparam] > weld_drawing_area(const OString& id, const a11yref& rA11yImpl = nullptr, > ^~~~~~~ > MaterializeTemporaryExpr 0x7fdff3111360 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' lvalue > `-CXXBindTemporaryExpr 0x7fdff3111340 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' (CXXTemporary 0x7fdff3111340) > `-CXXConstructExpr 0x7fdff3111308 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' 'void (class com::sun::star::accessibility::XAccessible *)' > `-ImplicitCastExpr 0x7fdff31112f0 'class com::sun::star::accessibility::XAccessible *' <NullToPointer> > `-CXXNullPtrLiteralExpr 0x7fdff31112b0 'nullptr_t' > MaterializeTemporaryExpr 0x7fdff39275d8 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' lvalue > `-CXXBindTemporaryExpr 0x7fdff39275b8 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' (CXXTemporary 0x7fdff39275b8) > `-CXXConstructExpr 0x7fdff3927580 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' 'void (class com::sun::star::accessibility::XAccessible *)' > `-ImplicitCastExpr 0x7fdff3927568 'class com::sun::star::accessibility::XAccessible *' <NullToPointer> > `-CXXNullPtrLiteralExpr 0x7fdff3927528 'nullptr_t' Change-Id: I2603180571df2248d4697ba63e1a8391d0dfc596 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/89157 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-02-20 21:04:11 +01:00
case Stmt::CXXBindTemporaryExprClass:
case Stmt::ParenExprClass:
break;
Adapt structurallyIdentical to current needs ...since aad94d48b19135a2e46ac7b2c0f41f3bb29bb5a7 "Silence warnings in salvtables.hxx", where --enable-compiler-plugins=debug had started to fail with > In file included from vcl/source/app/salvtables.cxx:75: > vcl/inc/salvtables.hxx:128:69: fatal error: TODO: Unexpected 'IdenticalDefaultArgumentsResult::Maybe' [loplugin:overrideparam] > weld_drawing_area(const OString& id, const a11yref& rA11yImpl = nullptr, > ^~~~~~~ > include/vcl/weld.hxx:2138:69: note: TODO: second argument is here [loplugin:overrideparam] > weld_drawing_area(const OString& id, const a11yref& rA11yImpl = nullptr, > ^~~~~~~ > MaterializeTemporaryExpr 0x7fdff3111360 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' lvalue > `-CXXBindTemporaryExpr 0x7fdff3111340 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' (CXXTemporary 0x7fdff3111340) > `-CXXConstructExpr 0x7fdff3111308 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' 'void (class com::sun::star::accessibility::XAccessible *)' > `-ImplicitCastExpr 0x7fdff31112f0 'class com::sun::star::accessibility::XAccessible *' <NullToPointer> > `-CXXNullPtrLiteralExpr 0x7fdff31112b0 'nullptr_t' > MaterializeTemporaryExpr 0x7fdff39275d8 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' lvalue > `-CXXBindTemporaryExpr 0x7fdff39275b8 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' (CXXTemporary 0x7fdff39275b8) > `-CXXConstructExpr 0x7fdff3927580 'const a11yref':'const class com::sun::star::uno::Reference<class com::sun::star::accessibility::XAccessible>' 'void (class com::sun::star::accessibility::XAccessible *)' > `-ImplicitCastExpr 0x7fdff3927568 'class com::sun::star::accessibility::XAccessible *' <NullToPointer> > `-CXXNullPtrLiteralExpr 0x7fdff3927528 'nullptr_t' Change-Id: I2603180571df2248d4697ba63e1a8391d0dfc596 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/89157 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-02-20 21:04:11 +01:00
case Stmt::CXXNullPtrLiteralExprClass:
return true;
default:
// Conservatively assume non-identical for expressions that don't happen for us in practice
// when compiling the LO code base (and for which the above set of supported classes would
// need to be extended):
return false;
}
auto i1 = stmt1->child_begin();
auto e1 = stmt1->child_end();
auto i2 = stmt2->child_begin();
auto e2 = stmt2->child_end();
for (; i1 != e1; ++i1, ++i2) {
if (i2 == e2 || !structurallyIdentical(*i1, *i2)) {
return false;
}
}
return i2 == e2;
}
}
Plugin::Plugin( const InstantiationData& data )
: compiler( data.compiler ), handler( data.handler ), name( data.name )
{
}
DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc ) const
{
return handler.report( level, name, message, compiler, loc );
}
void normalizeDotDotInFilePath( std::string & s )
{
for (std::string::size_type i = 0;;)
{
i = s.find("/.", i);
if (i == std::string::npos) {
break;
}
if (i + 2 == s.length() || s[i + 2] == '/') {
s.erase(i, 2); // [AAA]/.[/CCC] -> [AAA][/CCC]
} else if (s[i + 2] == '.'
&& (i + 3 == s.length() || s[i + 3] == '/'))
{
if (i == 0) { // /..[/CCC] -> /..[/CCC]
break;
}
auto j = s.rfind('/', i - 1);
if (j == std::string::npos)
{
// BBB/..[/CCC] -> BBB/..[/CCC] (instead of BBB/../CCC ->
// CCC, to avoid wrong ../../CCC -> CCC; relative paths
// shouldn't happen anyway, and even if they did, wouldn't
// match against WORKDIR anyway, as WORKDIR should be
// absolute):
break;
}
s.erase(j, i + 3 - j); // AAA/BBB/..[/CCC] -> AAA[/CCC]
i = j;
} else {
i += 2;
}
}
}
make (some) clang plugins share the same RecursiveASTVisitor Each plugin currently uses its own recursive AST run, which adds up. This patch adds another shared plugin which internally contains all (suitable) plugins and dispatches to them from the same one recursive run. This patch converts ~25 plugins and for starmath's accessibility.cxx reduces clang build time from 5.43s to 5.14s (and it's 4.39s without any plugins). As there are almost 50 more plugins to go, this can theoretically result in 4.56s final time, although probably not all plugins can be that easily converted, if at all. This mostly requires very little change in many plugins (see e.g. BadStatics), some even work without any functionality change (e.g. CharRightShift). Traverse* calls require some changes but are often not that difficult. WalkUp* probably can't be supported, although some plugins can(?) possibly be adjusted to not rely on them. And of course some plugins can be left as they are, using their own recursive run. See description at the top of generator.cxx for description of how to convert a plugin. The sharedvisitor.cxx source is generated based on scanning relevant plugin sources using a clang-based scanner/generator. The generated source is intentionally included instead of getting always generated, as the generating currently takes some time, so it should get updated in git whenever a change in a plugin triggers a source change in it. Change-Id: Ia0d2e3a5a464659503dbb4ed6c20b6cc89b4de01 Reviewed-on: https://gerrit.libreoffice.org/68026 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk> Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
2019-03-07 14:31:17 +01:00
void Plugin::registerPlugin( Plugin* (*create)( const InstantiationData& ), const char* optionName,
bool isPPCallback, bool isSharedPlugin, bool byDefault )
{
make (some) clang plugins share the same RecursiveASTVisitor Each plugin currently uses its own recursive AST run, which adds up. This patch adds another shared plugin which internally contains all (suitable) plugins and dispatches to them from the same one recursive run. This patch converts ~25 plugins and for starmath's accessibility.cxx reduces clang build time from 5.43s to 5.14s (and it's 4.39s without any plugins). As there are almost 50 more plugins to go, this can theoretically result in 4.56s final time, although probably not all plugins can be that easily converted, if at all. This mostly requires very little change in many plugins (see e.g. BadStatics), some even work without any functionality change (e.g. CharRightShift). Traverse* calls require some changes but are often not that difficult. WalkUp* probably can't be supported, although some plugins can(?) possibly be adjusted to not rely on them. And of course some plugins can be left as they are, using their own recursive run. See description at the top of generator.cxx for description of how to convert a plugin. The sharedvisitor.cxx source is generated based on scanning relevant plugin sources using a clang-based scanner/generator. The generated source is intentionally included instead of getting always generated, as the generating currently takes some time, so it should get updated in git whenever a change in a plugin triggers a source change in it. Change-Id: Ia0d2e3a5a464659503dbb4ed6c20b6cc89b4de01 Reviewed-on: https://gerrit.libreoffice.org/68026 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk> Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
2019-03-07 14:31:17 +01:00
PluginHandler::registerPlugin( create, optionName, isPPCallback, isSharedPlugin, byDefault );
}
bool Plugin::evaluate(const Expr* expr, APSInt& x)
{
if (compat::EvaluateAsInt(expr, x, compiler.getASTContext()))
{
return true;
}
if (isa<CXXNullPtrLiteralExpr>(expr)) {
x = 0;
return true;
}
return false;
}
Adapt to changed clang::ASTContext::getParents behavior on Clang 11 trunk ...since <https://github.com/llvm/llvm-project/commit/ d0da5d2bbe8305d06dc01a98706fd73e11e24a9f> "Change default traversal in AST Matchers to ignore invisible nodes". This caused failures > [CPT] compilerplugins/clang/test/constparams.cxx > ParmVarDecl 0x11d76c730 <compilerplugins/clang/test/constparams.cxx:15:12, col:18> col:18 used f1 'int *' > DeclRefExpr 0x11d76c948 'int *' lvalue ParmVar 0x11d76c730 'f1' 'int *' > ParmVarDecl 0x11d76cc80 <compilerplugins/clang/test/constparams.cxx:21:12, col:18> col:18 used f2 'int *' > DeclRefExpr 0x11d76ce60 'int *' lvalue ParmVar 0x11d76cc80 'f2' 'int *' > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/constparams.cxx Line 15: this parameter can be const Class1::Class1 [loplugin:constparams] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/constparams.cxx Line 15: no parent? [loplugin:constparams] > File compilerplugins/clang/test/constparams.cxx Line 21: no parent? [loplugin:constparams] > 3 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedenumconstants.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: read Bottom [loplugin:unusedenumconstants] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: write Bottom [loplugin:unusedenumconstants] > 2 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedfields.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedfields.cxx Line 156 (directive at compilerplugins/clang/test/unusedfields.cxx:164): write m_f5 [loplugin:unusedfields] > File compilerplugins/clang/test/unusedfields.cxx Line 210 (directive at compilerplugins/clang/test/unusedfields.cxx:211): read m_f1 [loplugin:unusedfields] > 2 errors generated. For compilerplugins/clang/test/constparams.cxx at least it would have worked to fix that locally with > diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx > index 95c8184009d7..70f056fa5a69 100644 > --- a/compilerplugins/clang/constparams.cxx > +++ b/compilerplugins/clang/constparams.cxx > @@ -274,7 +274,7 @@ bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVar > { > for ( auto cxxCtorInitializer : cxxConstructorDecl->inits()) > { > - if ( cxxCtorInitializer->getInit() == stmt) > + if ( cxxCtorInitializer->getInit()->IgnoreImpCasts() == stmt) > { > if (cxxCtorInitializer->isAnyMemberInitializer()) > { (somewhat unintuitively, given the Clang change is apparently about ignoring more implicit nodes), but overall it appears better---at least for now---to use a getParents variant that keeps the old traversal behavior. For that, instead of using the clang::ASTContext::ParentMapCtx, we create our own loplugin::Plugin::parentMapContext_. There appear to be no uses of ASTContext::getParent across the Clang codebase itself, outside of ASTMatcher code, so it looks unlikely that creating our own ParentMapContext instance would degrade performance by no longer sharing cached data between Clang's internals and our plugin. (And given that ASTContext::getParents is deprecated with the note "New callers should use ParentMapContext::getParents() directly", this may well be the correct way in the long run, anyway.) Change-Id: I46c7912f2737e7c224fd45ab41441f69e2f10bd4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94795 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-05-25 15:50:21 +02:00
compat::DynTypedNodeList Plugin::getParents(Decl const & decl)
{
#if CLANG_VERSION >= 110000
if (!parentMapContext_) {
parentMapContext_.reset(new ParentMapContext(compiler.getASTContext()));
parentMapContext_->setTraversalKind(TK_AsIs);
}
return parentMapContext_->getParents(decl);
#else
return compiler.getASTContext().getParents(decl);
#endif
}
compat::DynTypedNodeList Plugin::getParents(Stmt const & stmt)
{
#if CLANG_VERSION >= 110000
if (!parentMapContext_) {
parentMapContext_.reset(new ParentMapContext(compiler.getASTContext()));
parentMapContext_->setTraversalKind(TK_AsIs);
}
return parentMapContext_->getParents(stmt);
#else
return compiler.getASTContext().getParents(stmt);
#endif
}
const Stmt* Plugin::getParentStmt( const Stmt* stmt )
{
Adapt to changed clang::ASTContext::getParents behavior on Clang 11 trunk ...since <https://github.com/llvm/llvm-project/commit/ d0da5d2bbe8305d06dc01a98706fd73e11e24a9f> "Change default traversal in AST Matchers to ignore invisible nodes". This caused failures > [CPT] compilerplugins/clang/test/constparams.cxx > ParmVarDecl 0x11d76c730 <compilerplugins/clang/test/constparams.cxx:15:12, col:18> col:18 used f1 'int *' > DeclRefExpr 0x11d76c948 'int *' lvalue ParmVar 0x11d76c730 'f1' 'int *' > ParmVarDecl 0x11d76cc80 <compilerplugins/clang/test/constparams.cxx:21:12, col:18> col:18 used f2 'int *' > DeclRefExpr 0x11d76ce60 'int *' lvalue ParmVar 0x11d76cc80 'f2' 'int *' > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/constparams.cxx Line 15: this parameter can be const Class1::Class1 [loplugin:constparams] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/constparams.cxx Line 15: no parent? [loplugin:constparams] > File compilerplugins/clang/test/constparams.cxx Line 21: no parent? [loplugin:constparams] > 3 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedenumconstants.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: read Bottom [loplugin:unusedenumconstants] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: write Bottom [loplugin:unusedenumconstants] > 2 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedfields.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedfields.cxx Line 156 (directive at compilerplugins/clang/test/unusedfields.cxx:164): write m_f5 [loplugin:unusedfields] > File compilerplugins/clang/test/unusedfields.cxx Line 210 (directive at compilerplugins/clang/test/unusedfields.cxx:211): read m_f1 [loplugin:unusedfields] > 2 errors generated. For compilerplugins/clang/test/constparams.cxx at least it would have worked to fix that locally with > diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx > index 95c8184009d7..70f056fa5a69 100644 > --- a/compilerplugins/clang/constparams.cxx > +++ b/compilerplugins/clang/constparams.cxx > @@ -274,7 +274,7 @@ bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVar > { > for ( auto cxxCtorInitializer : cxxConstructorDecl->inits()) > { > - if ( cxxCtorInitializer->getInit() == stmt) > + if ( cxxCtorInitializer->getInit()->IgnoreImpCasts() == stmt) > { > if (cxxCtorInitializer->isAnyMemberInitializer()) > { (somewhat unintuitively, given the Clang change is apparently about ignoring more implicit nodes), but overall it appears better---at least for now---to use a getParents variant that keeps the old traversal behavior. For that, instead of using the clang::ASTContext::ParentMapCtx, we create our own loplugin::Plugin::parentMapContext_. There appear to be no uses of ASTContext::getParent across the Clang codebase itself, outside of ASTMatcher code, so it looks unlikely that creating our own ParentMapContext instance would degrade performance by no longer sharing cached data between Clang's internals and our plugin. (And given that ASTContext::getParents is deprecated with the note "New callers should use ParentMapContext::getParents() directly", this may well be the correct way in the long run, anyway.) Change-Id: I46c7912f2737e7c224fd45ab41441f69e2f10bd4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94795 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-05-25 15:50:21 +02:00
auto parentsRange = getParents(*stmt);
if ( parentsRange.begin() == parentsRange.end())
return nullptr;
return parentsRange.begin()->get<Stmt>();
}
Stmt* Plugin::getParentStmt( Stmt* stmt )
{
Adapt to changed clang::ASTContext::getParents behavior on Clang 11 trunk ...since <https://github.com/llvm/llvm-project/commit/ d0da5d2bbe8305d06dc01a98706fd73e11e24a9f> "Change default traversal in AST Matchers to ignore invisible nodes". This caused failures > [CPT] compilerplugins/clang/test/constparams.cxx > ParmVarDecl 0x11d76c730 <compilerplugins/clang/test/constparams.cxx:15:12, col:18> col:18 used f1 'int *' > DeclRefExpr 0x11d76c948 'int *' lvalue ParmVar 0x11d76c730 'f1' 'int *' > ParmVarDecl 0x11d76cc80 <compilerplugins/clang/test/constparams.cxx:21:12, col:18> col:18 used f2 'int *' > DeclRefExpr 0x11d76ce60 'int *' lvalue ParmVar 0x11d76cc80 'f2' 'int *' > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/constparams.cxx Line 15: this parameter can be const Class1::Class1 [loplugin:constparams] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/constparams.cxx Line 15: no parent? [loplugin:constparams] > File compilerplugins/clang/test/constparams.cxx Line 21: no parent? [loplugin:constparams] > 3 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedenumconstants.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: read Bottom [loplugin:unusedenumconstants] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: write Bottom [loplugin:unusedenumconstants] > 2 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedfields.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedfields.cxx Line 156 (directive at compilerplugins/clang/test/unusedfields.cxx:164): write m_f5 [loplugin:unusedfields] > File compilerplugins/clang/test/unusedfields.cxx Line 210 (directive at compilerplugins/clang/test/unusedfields.cxx:211): read m_f1 [loplugin:unusedfields] > 2 errors generated. For compilerplugins/clang/test/constparams.cxx at least it would have worked to fix that locally with > diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx > index 95c8184009d7..70f056fa5a69 100644 > --- a/compilerplugins/clang/constparams.cxx > +++ b/compilerplugins/clang/constparams.cxx > @@ -274,7 +274,7 @@ bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVar > { > for ( auto cxxCtorInitializer : cxxConstructorDecl->inits()) > { > - if ( cxxCtorInitializer->getInit() == stmt) > + if ( cxxCtorInitializer->getInit()->IgnoreImpCasts() == stmt) > { > if (cxxCtorInitializer->isAnyMemberInitializer()) > { (somewhat unintuitively, given the Clang change is apparently about ignoring more implicit nodes), but overall it appears better---at least for now---to use a getParents variant that keeps the old traversal behavior. For that, instead of using the clang::ASTContext::ParentMapCtx, we create our own loplugin::Plugin::parentMapContext_. There appear to be no uses of ASTContext::getParent across the Clang codebase itself, outside of ASTMatcher code, so it looks unlikely that creating our own ParentMapContext instance would degrade performance by no longer sharing cached data between Clang's internals and our plugin. (And given that ASTContext::getParents is deprecated with the note "New callers should use ParentMapContext::getParents() directly", this may well be the correct way in the long run, anyway.) Change-Id: I46c7912f2737e7c224fd45ab41441f69e2f10bd4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94795 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-05-25 15:50:21 +02:00
auto parentsRange = getParents(*stmt);
if ( parentsRange.begin() == parentsRange.end())
return nullptr;
return const_cast<Stmt*>(parentsRange.begin()->get<Stmt>());
}
Adapt to changed clang::ASTContext::getParents behavior on Clang 11 trunk ...since <https://github.com/llvm/llvm-project/commit/ d0da5d2bbe8305d06dc01a98706fd73e11e24a9f> "Change default traversal in AST Matchers to ignore invisible nodes". This caused failures > [CPT] compilerplugins/clang/test/constparams.cxx > ParmVarDecl 0x11d76c730 <compilerplugins/clang/test/constparams.cxx:15:12, col:18> col:18 used f1 'int *' > DeclRefExpr 0x11d76c948 'int *' lvalue ParmVar 0x11d76c730 'f1' 'int *' > ParmVarDecl 0x11d76cc80 <compilerplugins/clang/test/constparams.cxx:21:12, col:18> col:18 used f2 'int *' > DeclRefExpr 0x11d76ce60 'int *' lvalue ParmVar 0x11d76cc80 'f2' 'int *' > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/constparams.cxx Line 15: this parameter can be const Class1::Class1 [loplugin:constparams] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/constparams.cxx Line 15: no parent? [loplugin:constparams] > File compilerplugins/clang/test/constparams.cxx Line 21: no parent? [loplugin:constparams] > 3 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedenumconstants.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: read Bottom [loplugin:unusedenumconstants] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: write Bottom [loplugin:unusedenumconstants] > 2 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedfields.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedfields.cxx Line 156 (directive at compilerplugins/clang/test/unusedfields.cxx:164): write m_f5 [loplugin:unusedfields] > File compilerplugins/clang/test/unusedfields.cxx Line 210 (directive at compilerplugins/clang/test/unusedfields.cxx:211): read m_f1 [loplugin:unusedfields] > 2 errors generated. For compilerplugins/clang/test/constparams.cxx at least it would have worked to fix that locally with > diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx > index 95c8184009d7..70f056fa5a69 100644 > --- a/compilerplugins/clang/constparams.cxx > +++ b/compilerplugins/clang/constparams.cxx > @@ -274,7 +274,7 @@ bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVar > { > for ( auto cxxCtorInitializer : cxxConstructorDecl->inits()) > { > - if ( cxxCtorInitializer->getInit() == stmt) > + if ( cxxCtorInitializer->getInit()->IgnoreImpCasts() == stmt) > { > if (cxxCtorInitializer->isAnyMemberInitializer()) > { (somewhat unintuitively, given the Clang change is apparently about ignoring more implicit nodes), but overall it appears better---at least for now---to use a getParents variant that keeps the old traversal behavior. For that, instead of using the clang::ASTContext::ParentMapCtx, we create our own loplugin::Plugin::parentMapContext_. There appear to be no uses of ASTContext::getParent across the Clang codebase itself, outside of ASTMatcher code, so it looks unlikely that creating our own ParentMapContext instance would degrade performance by no longer sharing cached data between Clang's internals and our plugin. (And given that ASTContext::getParents is deprecated with the note "New callers should use ParentMapContext::getParents() directly", this may well be the correct way in the long run, anyway.) Change-Id: I46c7912f2737e7c224fd45ab41441f69e2f10bd4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94795 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-05-25 15:50:21 +02:00
const Decl* Plugin::getFunctionDeclContext(const Stmt* stmt)
{
Adapt to changed clang::ASTContext::getParents behavior on Clang 11 trunk ...since <https://github.com/llvm/llvm-project/commit/ d0da5d2bbe8305d06dc01a98706fd73e11e24a9f> "Change default traversal in AST Matchers to ignore invisible nodes". This caused failures > [CPT] compilerplugins/clang/test/constparams.cxx > ParmVarDecl 0x11d76c730 <compilerplugins/clang/test/constparams.cxx:15:12, col:18> col:18 used f1 'int *' > DeclRefExpr 0x11d76c948 'int *' lvalue ParmVar 0x11d76c730 'f1' 'int *' > ParmVarDecl 0x11d76cc80 <compilerplugins/clang/test/constparams.cxx:21:12, col:18> col:18 used f2 'int *' > DeclRefExpr 0x11d76ce60 'int *' lvalue ParmVar 0x11d76cc80 'f2' 'int *' > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/constparams.cxx Line 15: this parameter can be const Class1::Class1 [loplugin:constparams] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/constparams.cxx Line 15: no parent? [loplugin:constparams] > File compilerplugins/clang/test/constparams.cxx Line 21: no parent? [loplugin:constparams] > 3 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedenumconstants.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: read Bottom [loplugin:unusedenumconstants] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: write Bottom [loplugin:unusedenumconstants] > 2 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedfields.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedfields.cxx Line 156 (directive at compilerplugins/clang/test/unusedfields.cxx:164): write m_f5 [loplugin:unusedfields] > File compilerplugins/clang/test/unusedfields.cxx Line 210 (directive at compilerplugins/clang/test/unusedfields.cxx:211): read m_f1 [loplugin:unusedfields] > 2 errors generated. For compilerplugins/clang/test/constparams.cxx at least it would have worked to fix that locally with > diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx > index 95c8184009d7..70f056fa5a69 100644 > --- a/compilerplugins/clang/constparams.cxx > +++ b/compilerplugins/clang/constparams.cxx > @@ -274,7 +274,7 @@ bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVar > { > for ( auto cxxCtorInitializer : cxxConstructorDecl->inits()) > { > - if ( cxxCtorInitializer->getInit() == stmt) > + if ( cxxCtorInitializer->getInit()->IgnoreImpCasts() == stmt) > { > if (cxxCtorInitializer->isAnyMemberInitializer()) > { (somewhat unintuitively, given the Clang change is apparently about ignoring more implicit nodes), but overall it appears better---at least for now---to use a getParents variant that keeps the old traversal behavior. For that, instead of using the clang::ASTContext::ParentMapCtx, we create our own loplugin::Plugin::parentMapContext_. There appear to be no uses of ASTContext::getParent across the Clang codebase itself, outside of ASTMatcher code, so it looks unlikely that creating our own ParentMapContext instance would degrade performance by no longer sharing cached data between Clang's internals and our plugin. (And given that ASTContext::getParents is deprecated with the note "New callers should use ParentMapContext::getParents() directly", this may well be the correct way in the long run, anyway.) Change-Id: I46c7912f2737e7c224fd45ab41441f69e2f10bd4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94795 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-05-25 15:50:21 +02:00
auto const parents = getParents(*stmt);
auto it = parents.begin();
if (it == parents.end())
return nullptr;
const Decl *decl = it->get<Decl>();
if (decl)
{
if (isa<VarDecl>(decl))
return dyn_cast<FunctionDecl>(decl->getDeclContext());
return decl;
}
stmt = it->get<Stmt>();
if (stmt)
Adapt to changed clang::ASTContext::getParents behavior on Clang 11 trunk ...since <https://github.com/llvm/llvm-project/commit/ d0da5d2bbe8305d06dc01a98706fd73e11e24a9f> "Change default traversal in AST Matchers to ignore invisible nodes". This caused failures > [CPT] compilerplugins/clang/test/constparams.cxx > ParmVarDecl 0x11d76c730 <compilerplugins/clang/test/constparams.cxx:15:12, col:18> col:18 used f1 'int *' > DeclRefExpr 0x11d76c948 'int *' lvalue ParmVar 0x11d76c730 'f1' 'int *' > ParmVarDecl 0x11d76cc80 <compilerplugins/clang/test/constparams.cxx:21:12, col:18> col:18 used f2 'int *' > DeclRefExpr 0x11d76ce60 'int *' lvalue ParmVar 0x11d76cc80 'f2' 'int *' > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/constparams.cxx Line 15: this parameter can be const Class1::Class1 [loplugin:constparams] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/constparams.cxx Line 15: no parent? [loplugin:constparams] > File compilerplugins/clang/test/constparams.cxx Line 21: no parent? [loplugin:constparams] > 3 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedenumconstants.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: read Bottom [loplugin:unusedenumconstants] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: write Bottom [loplugin:unusedenumconstants] > 2 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedfields.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedfields.cxx Line 156 (directive at compilerplugins/clang/test/unusedfields.cxx:164): write m_f5 [loplugin:unusedfields] > File compilerplugins/clang/test/unusedfields.cxx Line 210 (directive at compilerplugins/clang/test/unusedfields.cxx:211): read m_f1 [loplugin:unusedfields] > 2 errors generated. For compilerplugins/clang/test/constparams.cxx at least it would have worked to fix that locally with > diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx > index 95c8184009d7..70f056fa5a69 100644 > --- a/compilerplugins/clang/constparams.cxx > +++ b/compilerplugins/clang/constparams.cxx > @@ -274,7 +274,7 @@ bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVar > { > for ( auto cxxCtorInitializer : cxxConstructorDecl->inits()) > { > - if ( cxxCtorInitializer->getInit() == stmt) > + if ( cxxCtorInitializer->getInit()->IgnoreImpCasts() == stmt) > { > if (cxxCtorInitializer->isAnyMemberInitializer()) > { (somewhat unintuitively, given the Clang change is apparently about ignoring more implicit nodes), but overall it appears better---at least for now---to use a getParents variant that keeps the old traversal behavior. For that, instead of using the clang::ASTContext::ParentMapCtx, we create our own loplugin::Plugin::parentMapContext_. There appear to be no uses of ASTContext::getParent across the Clang codebase itself, outside of ASTMatcher code, so it looks unlikely that creating our own ParentMapContext instance would degrade performance by no longer sharing cached data between Clang's internals and our plugin. (And given that ASTContext::getParents is deprecated with the note "New callers should use ParentMapContext::getParents() directly", this may well be the correct way in the long run, anyway.) Change-Id: I46c7912f2737e7c224fd45ab41441f69e2f10bd4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94795 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-05-25 15:50:21 +02:00
return getFunctionDeclContext(stmt);
return nullptr;
}
const FunctionDecl* Plugin::getParentFunctionDecl( const Stmt* stmt )
{
Adapt to changed clang::ASTContext::getParents behavior on Clang 11 trunk ...since <https://github.com/llvm/llvm-project/commit/ d0da5d2bbe8305d06dc01a98706fd73e11e24a9f> "Change default traversal in AST Matchers to ignore invisible nodes". This caused failures > [CPT] compilerplugins/clang/test/constparams.cxx > ParmVarDecl 0x11d76c730 <compilerplugins/clang/test/constparams.cxx:15:12, col:18> col:18 used f1 'int *' > DeclRefExpr 0x11d76c948 'int *' lvalue ParmVar 0x11d76c730 'f1' 'int *' > ParmVarDecl 0x11d76cc80 <compilerplugins/clang/test/constparams.cxx:21:12, col:18> col:18 used f2 'int *' > DeclRefExpr 0x11d76ce60 'int *' lvalue ParmVar 0x11d76cc80 'f2' 'int *' > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/constparams.cxx Line 15: this parameter can be const Class1::Class1 [loplugin:constparams] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/constparams.cxx Line 15: no parent? [loplugin:constparams] > File compilerplugins/clang/test/constparams.cxx Line 21: no parent? [loplugin:constparams] > 3 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedenumconstants.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: read Bottom [loplugin:unusedenumconstants] > error: 'error' diagnostics seen but not expected: > File compilerplugins/clang/test/unusedenumconstants.cxx Line 30: write Bottom [loplugin:unusedenumconstants] > 2 errors generated. [...] > [CPT] compilerplugins/clang/test/unusedfields.cxx > error: 'error' diagnostics expected but not seen: > File compilerplugins/clang/test/unusedfields.cxx Line 156 (directive at compilerplugins/clang/test/unusedfields.cxx:164): write m_f5 [loplugin:unusedfields] > File compilerplugins/clang/test/unusedfields.cxx Line 210 (directive at compilerplugins/clang/test/unusedfields.cxx:211): read m_f1 [loplugin:unusedfields] > 2 errors generated. For compilerplugins/clang/test/constparams.cxx at least it would have worked to fix that locally with > diff --git a/compilerplugins/clang/constparams.cxx b/compilerplugins/clang/constparams.cxx > index 95c8184009d7..70f056fa5a69 100644 > --- a/compilerplugins/clang/constparams.cxx > +++ b/compilerplugins/clang/constparams.cxx > @@ -274,7 +274,7 @@ bool ConstParams::checkIfCanBeConst(const Stmt* stmt, const ParmVarDecl* parmVar > { > for ( auto cxxCtorInitializer : cxxConstructorDecl->inits()) > { > - if ( cxxCtorInitializer->getInit() == stmt) > + if ( cxxCtorInitializer->getInit()->IgnoreImpCasts() == stmt) > { > if (cxxCtorInitializer->isAnyMemberInitializer()) > { (somewhat unintuitively, given the Clang change is apparently about ignoring more implicit nodes), but overall it appears better---at least for now---to use a getParents variant that keeps the old traversal behavior. For that, instead of using the clang::ASTContext::ParentMapCtx, we create our own loplugin::Plugin::parentMapContext_. There appear to be no uses of ASTContext::getParent across the Clang codebase itself, outside of ASTMatcher code, so it looks unlikely that creating our own ParentMapContext instance would degrade performance by no longer sharing cached data between Clang's internals and our plugin. (And given that ASTContext::getParents is deprecated with the note "New callers should use ParentMapContext::getParents() directly", this may well be the correct way in the long run, anyway.) Change-Id: I46c7912f2737e7c224fd45ab41441f69e2f10bd4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94795 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2020-05-25 15:50:21 +02:00
const Decl *decl = getFunctionDeclContext(stmt);
if (decl)
return static_cast<const FunctionDecl*>(decl->getNonClosureContext());
return nullptr;
}
StringRef Plugin::getFilenameOfLocation(SourceLocation spellingLocation) const
{
// prevent crashes when running the global-analysis plugins
if (!spellingLocation.isValid())
return "";
static enum { NOINIT, STDIN, GOOD } s_Mode(NOINIT);
if (s_Mode == GOOD)
{
return compiler.getSourceManager().getFilename(spellingLocation);
}
else if (s_Mode == STDIN
|| !compiler.getSourceManager().isInMainFile(spellingLocation))
{
const char* bufferName = compiler.getSourceManager().getPresumedLoc(spellingLocation).getFilename();
return bufferName;
}
else
{
auto const fn(compiler.getSourceManager().getFilename(spellingLocation));
if (!fn.data()) // wtf? happens in sot/source/sdstor/stg.cxx
{
return fn;
}
#if !defined _WIN32
assert(fn.startswith("/") || fn == "<stdin>");
#endif
s_Mode = fn == "<stdin>" ? STDIN : GOOD;
return getFilenameOfLocation(spellingLocation);
}
}
bool Plugin::isInUnoIncludeFile(SourceLocation spellingLocation) const
{
StringRef name{ getFilenameOfLocation(spellingLocation) };
return compiler.getSourceManager().isInMainFile(spellingLocation)
? (isSamePathname(name, SRCDIR "/cppu/source/cppu/compat.cxx")
|| isSamePathname(name, SRCDIR "/cppuhelper/source/compat.cxx")
|| isSamePathname(name, SRCDIR "/sal/osl/all/compat.cxx"))
: (hasPathnamePrefix(name, SRCDIR "/include/com/")
|| hasPathnamePrefix(name, SRCDIR "/include/cppu/")
|| hasPathnamePrefix(name, SRCDIR "/include/cppuhelper/")
|| hasPathnamePrefix(name, SRCDIR "/include/osl/")
|| hasPathnamePrefix(name, SRCDIR "/include/rtl/")
|| hasPathnamePrefix(name, SRCDIR "/include/sal/")
|| hasPathnamePrefix(name, SRCDIR "/include/salhelper/")
|| hasPathnamePrefix(name, SRCDIR "/include/systools/")
|| hasPathnamePrefix(name, SRCDIR "/include/typelib/")
|| hasPathnamePrefix(name, SRCDIR "/include/uno/"));
}
bool Plugin::isInUnoIncludeFile(const FunctionDecl* functionDecl) const
{
return isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(
functionDecl->getCanonicalDecl()->getNameInfo().getLoc()));
}
SourceLocation Plugin::locationAfterToken( SourceLocation location )
{
return Lexer::getLocForEndOfToken( location, 0, compiler.getSourceManager(), compiler.getLangOpts());
}
bool Plugin::isUnitTestMode()
{
return PluginHandler::isUnitTestMode();
}
bool Plugin::containsPreprocessingConditionalInclusion(SourceRange range)
{
// Preprocessing directives (other than _Pragma, which is not relevant here) cannot appear in
// macro expansions, so it is safe to just consider the range of expansion locations:
auto const begin = compiler.getSourceManager().getExpansionLoc(
range.getBegin());
auto const end = compiler.getSourceManager().getExpansionLoc(
range.getEnd());
assert(begin.isFileID() && end.isFileID());
if (!(begin == end
|| compiler.getSourceManager().isBeforeInTranslationUnit(
begin, end)))
{
if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal,
("unexpected broken range for Plugin::containsPreprocessingConditionalInclusion,"
" case 1"),
range.getBegin())
<< range;
}
// Conservatively assume "yes" if lexing fails:
return true;
}
auto hash = false;
for (auto loc = begin;;) {
Token tok;
if (Lexer::getRawToken(
loc, tok, compiler.getSourceManager(),
compiler.getLangOpts(), true))
{
if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal,
("unexpected broken range for"
" Plugin::containsPreprocessingConditionalInclusion, case 2"),
loc)
<< range;
}
// Conservatively assume "yes" if lexing fails:
return true;
}
if (hash && tok.is(tok::raw_identifier)) {
auto const id = tok.getRawIdentifier();
if (id == "if" || id == "ifdef" || id == "ifndef"
|| id == "elif" || id == "else" || id == "endif")
{
return true;
}
}
if (loc == end) {
break;
}
hash = tok.is(tok::hash) && tok.isAtStartOfLine();
loc = loc.getLocWithOffset(
std::max<unsigned>(
Lexer::MeasureTokenLength(
loc, compiler.getSourceManager(),
compiler.getLangOpts()),
1));
}
return false;
}
Plugin::IdenticalDefaultArgumentsResult Plugin::checkIdenticalDefaultArguments(
Expr const * argument1, Expr const * argument2)
{
if ((argument1 == nullptr) != (argument2 == nullptr)) {
return IdenticalDefaultArgumentsResult::No;
}
if (argument1 == nullptr) {
return IdenticalDefaultArgumentsResult::Yes;
}
if (argument1->isNullPointerConstant(compiler.getASTContext(), Expr::NPC_NeverValueDependent)
&& argument2->isNullPointerConstant(compiler.getASTContext(), Expr::NPC_NeverValueDependent))
{
return IdenticalDefaultArgumentsResult::Yes;
}
APSInt x1, x2;
if (evaluate(argument1, x1) && evaluate(argument2, x2))
{
return x1 == x2
? IdenticalDefaultArgumentsResult::Yes
: IdenticalDefaultArgumentsResult::No;
}
APFloat f1(0.0f), f2(0.0f);
if (argument1->EvaluateAsFloat(f1, compiler.getASTContext())
&& argument2->EvaluateAsFloat(f2, compiler.getASTContext()))
{
return f1.bitwiseIsEqual(f2)
? IdenticalDefaultArgumentsResult::Yes
: IdenticalDefaultArgumentsResult::No;
}
auto const desugared1 = argument1->IgnoreParenImpCasts();
auto const desugared2 = argument2->IgnoreParenImpCasts();
if (auto const lit1 = dyn_cast<clang::StringLiteral>(desugared1)) {
if (auto const lit2 = dyn_cast<clang::StringLiteral>(desugared2)) {
return lit1->getBytes() == lit2->getBytes()
? IdenticalDefaultArgumentsResult::Yes
: IdenticalDefaultArgumentsResult::No;
}
}
// catch params with defaults like "= OUString()"
for (Expr const * e1 = desugared1, * e2 = desugared2;;) {
auto const ce1 = dyn_cast<CXXConstructExpr>(skipImplicit(e1));
auto const ce2 = dyn_cast<CXXConstructExpr>(skipImplicit(e2));
if (ce1 == nullptr || ce2 == nullptr) {
break;
}
if (ce1->getConstructor()->getCanonicalDecl() != ce2->getConstructor()->getCanonicalDecl())
{
return IdenticalDefaultArgumentsResult::No;
}
if (ce1->isElidable() && ce2->isElidable() && ce1->getNumArgs() == 1
&& ce2->getNumArgs() == 1)
{
assert(ce1->getConstructor()->isCopyOrMoveConstructor());
e1 = ce1->getArg(0)->IgnoreImpCasts();
e2 = ce2->getArg(0)->IgnoreImpCasts();
continue;
}
if (ce1->getNumArgs() == 0 && ce2->getNumArgs() == 0) {
return IdenticalDefaultArgumentsResult::Yes;
}
break;
}
// If the EvaluateAsRValue derivatives above failed because the arguments use e.g. (constexpr)
// function template specializations that happen to not have been instantiated in this TU, try a
// structural comparison of the arguments:
if (structurallyIdentical(argument1, argument2)) {
return IdenticalDefaultArgumentsResult::Yes;
}
if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: Unexpected 'IdenticalDefaultArgumentsResult::Maybe'",
argument1->getExprLoc())
<< argument1->getSourceRange();
report(
DiagnosticsEngine::Note, "TODO: second argument is here", argument2->getExprLoc())
<< argument2->getSourceRange();
argument1->dump();
argument2->dump();
}
return IdenticalDefaultArgumentsResult::Maybe;
}
RewritePlugin::RewritePlugin( const InstantiationData& data )
: Plugin( data )
, rewriter( data.rewriter )
{
}
bool RewritePlugin::insertText( SourceLocation Loc, StringRef Str, bool InsertAfter, bool indentNewLines )
{
assert( rewriter );
if (wouldRewriteWorkdir(Loc))
return false;
SourceRange Range(SourceRange(Loc, Loc.getLocWithOffset(Str.size())));
if( !handler.checkOverlap( Range ) )
{
report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Range.getBegin());
return false;
}
if( rewriter->InsertText( Loc, Str, InsertAfter, indentNewLines ))
return reportEditFailure( Loc );
handler.addSourceModification(Range);
return true;
}
bool RewritePlugin::insertTextAfter( SourceLocation Loc, StringRef Str )
{
assert( rewriter );
if (wouldRewriteWorkdir(Loc))
return false;
SourceRange Range(SourceRange(Loc, Loc.getLocWithOffset(Str.size())));
if( !handler.checkOverlap( Range ) )
{
report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Range.getBegin());
return false;
}
if( rewriter->InsertTextAfter( Loc, Str ))
return reportEditFailure( Loc );
handler.addSourceModification(Range);
return true;
}
bool RewritePlugin::insertTextAfterToken( SourceLocation Loc, StringRef Str )
{
assert( rewriter );
if (wouldRewriteWorkdir(Loc))
return false;
SourceRange Range(SourceRange(Loc, Loc.getLocWithOffset(Str.size())));
if( !handler.checkOverlap( Range ) )
{
report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Range.getBegin());
return false;
}
if( rewriter->InsertTextAfterToken( Loc, Str ))
return reportEditFailure( Loc );
handler.addSourceModification(Range);
return true;
}
bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str )
{
assert( rewriter );
if (wouldRewriteWorkdir(Loc))
return false;
SourceRange Range(SourceRange(Loc, Loc.getLocWithOffset(Str.size())));
if( !handler.checkOverlap( Range ) )
{
report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", Range.getBegin());
return false;
}
if( rewriter->InsertTextBefore( Loc, Str ))
return reportEditFailure( Loc );
handler.addSourceModification(Range);
return true;
}
bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts )
{
CharSourceRange range( SourceRange( Start, Start.getLocWithOffset( Length )), false );
return removeText( range, opts );
}
bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts )
{
return removeText( CharSourceRange( range, true ), opts );
}
bool RewritePlugin::removeText( CharSourceRange range, RewriteOptions opts )
{
assert( rewriter );
if (wouldRewriteWorkdir(range.getBegin()))
return false;
if( rewriter->getRangeSize( range, opts ) == -1 )
return reportEditFailure( range.getBegin());
if( !handler.checkOverlap( range.getAsRange() ) )
{
report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin());
return false;
}
if( opts.flags & RemoveWholeStatement || opts.flags & RemoveAllWhitespace )
{
if( !adjustRangeForOptions( &range, opts ))
return reportEditFailure( range.getBegin());
}
if( rewriter->RemoveText( range, opts ))
return reportEditFailure( range.getBegin());
handler.addSourceModification(range.getAsRange());
return true;
}
bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts )
{
assert( rewriter );
SourceManager& SM = rewriter->getSourceMgr();
SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin()));
if( fileStartLoc.isInvalid())
return false;
bool isInvalid = false;
const char* fileBuf = SM.getCharacterData( fileStartLoc, &isInvalid );
if( isInvalid )
return false;
const char* startBuf = SM.getCharacterData( range->getBegin(), &isInvalid );
if( isInvalid )
return false;
SourceLocation locationEnd = range->getEnd();
if( range->isTokenRange())
locationEnd = locationAfterToken( locationEnd );
const char* endBuf = SM.getCharacterData( locationEnd, &isInvalid );
if( isInvalid )
return false;
const char* startPos = startBuf;
--startPos;
while( startPos >= fileBuf && ( *startPos == ' ' || *startPos == '\t' ))
--startPos;
if( startPos >= fileBuf && *startPos == '\n' )
startPos = startBuf - 1; // do not remove indentation whitespace (RemoveLineIfEmpty can do that)
const char* endPos = endBuf;
while( *endPos == ' ' || *endPos == '\t' )
++endPos;
if( opts.flags & RemoveWholeStatement )
{
if( *endPos == ';' )
++endPos;
else
return false;
}
*range = CharSourceRange( SourceRange( range->getBegin().getLocWithOffset( startPos - startBuf + 1 ),
locationEnd.getLocWithOffset( endPos - endBuf )), false );
return true;
}
bool RewritePlugin::replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr )
{
assert( rewriter );
if (wouldRewriteWorkdir(Start))
return false;
SourceRange Range(Start, Start.getLocWithOffset(std::max<size_t>(OrigLength, NewStr.size())));
if( OrigLength != 0 && !handler.checkOverlap( Range ) )
{
report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", Start );
return false;
}
if( rewriter->ReplaceText( Start, OrigLength, NewStr ))
return reportEditFailure( Start );
handler.addSourceModification(Range);
return true;
}
bool RewritePlugin::replaceText( SourceRange range, StringRef NewStr )
{
assert( rewriter );
if (wouldRewriteWorkdir(range.getBegin()))
return false;
if( rewriter->getRangeSize( range ) == -1 )
return reportEditFailure( range.getBegin());
if( !handler.checkOverlap( range ) )
{
report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", range.getBegin());
return false;
}
if( rewriter->ReplaceText( range, NewStr ))
return reportEditFailure( range.getBegin());
handler.addSourceModification(range);
return true;
}
bool RewritePlugin::replaceText( SourceRange range, SourceRange replacementRange )
{
assert( rewriter );
if (wouldRewriteWorkdir(range.getBegin()))
return false;
if( rewriter->getRangeSize( range ) == -1 )
return reportEditFailure( range.getBegin());
if( !handler.checkOverlap( range ) )
{
report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", range.getBegin());
return false;
}
if( rewriter->ReplaceText( range, replacementRange ))
return reportEditFailure( range.getBegin());
handler.addSourceModification(range);
return true;
}
bool RewritePlugin::wouldRewriteWorkdir(SourceLocation loc)
{
if (loc.isInvalid() || loc.isMacroID()) {
return false;
}
return
getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(loc))
.startswith(WORKDIR "/");
}
bool RewritePlugin::reportEditFailure( SourceLocation loc )
{
report( DiagnosticsEngine::Warning, "cannot perform source modification (macro expansion involved?)", loc );
return false;
}
namespace {
template<typename Fn> bool checkPathname(
StringRef pathname, StringRef against, Fn check)
{
if (check(pathname, against)) {
return true;
}
#if defined _WIN32
for (std::size_t n = 0;;)
{
std::size_t n1 = pathname.find('\\', n);
std::size_t n2 = against.find('\\', n);
if (n1 <= n2) {
if (n1 >= against.size()) {
return check(pathname.substr(n), against.substr(n));
}
if ((against[n1] != '/' && against[n1] != '\\')
|| pathname.substr(n, n1 - n) != against.substr(n, n1 - n))
{
break;
}
n = n1 + 1;
} else {
if (n2 >= pathname.size()) {
return check(pathname.substr(n), against.substr(n));
}
if (pathname[n2] != '/'
|| pathname.substr(n, n2 - n) != against.substr(n, n2 - n))
{
break;
}
n = n2 + 1;
}
}
#endif
return false;
}
}
bool hasPathnamePrefix(StringRef pathname, StringRef prefix)
{
return checkPathname(
pathname, prefix,
[](StringRef p, StringRef a) { return p.startswith(a); });
}
bool isSamePathname(StringRef pathname, StringRef other)
{
return checkPathname(
pathname, other, [](StringRef p, StringRef a) { return p == a; });
}
bool hasCLanguageLinkageType(FunctionDecl const * decl) {
assert(decl != nullptr);
if (decl->isExternC()) {
return true;
}
if (decl->isInExternCContext()) {
return true;
}
return false;
}
static const CXXRecordDecl* stripTypeSugar(QualType qt)
{
const clang::Type* t = qt.getTypePtr();
while (auto elaboratedType = dyn_cast<ElaboratedType>(t))
t = elaboratedType->desugar().getTypePtr();
auto recordType = dyn_cast<RecordType>(t);
if (!recordType)
return nullptr;
return dyn_cast_or_null<CXXRecordDecl>(recordType->getDecl());
}
int derivedFromCount(const CXXRecordDecl* subclassRecordDecl, const CXXRecordDecl* baseclassRecordDecl)
{
if (!subclassRecordDecl || !baseclassRecordDecl)
return 0;
int derivedCount = 0;
if (subclassRecordDecl == baseclassRecordDecl)
derivedCount++;
if (!subclassRecordDecl->hasDefinition())
return derivedCount;
for (auto it = subclassRecordDecl->bases_begin(); it != subclassRecordDecl->bases_end(); ++it)
{
derivedCount += derivedFromCount(stripTypeSugar(it->getType()), baseclassRecordDecl);
// short-circuit, we only care about 0,1,2
if (derivedCount > 1)
return derivedCount;
}
for (auto it = subclassRecordDecl->vbases_begin(); it != subclassRecordDecl->vbases_end(); ++it)
{
derivedCount += derivedFromCount(stripTypeSugar(it->getType()), baseclassRecordDecl);
// short-circuit, we only care about 0,1,2
if (derivedCount > 1)
return derivedCount;
}
return derivedCount;
}
int derivedFromCount(QualType subclassQt, QualType baseclassQt)
{
auto baseclassRecordDecl = stripTypeSugar(baseclassQt);
if (!baseclassRecordDecl)
return 0;
auto subclassRecordDecl = stripTypeSugar(subclassQt);
if (!subclassRecordDecl)
return 0;
return derivedFromCount(subclassRecordDecl, baseclassRecordDecl);
}
// It looks like Clang wrongly implements DR 4
// (<http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#4>) and treats
// a variable declared in an 'extern "..." {...}'-style linkage-specification as
// if it contained the 'extern' specifier:
bool hasExternalLinkage(VarDecl const * decl) {
if (decl->getLinkageAndVisibility().getLinkage() != ExternalLinkage) {
return false;
}
for (auto ctx = decl->getLexicalDeclContext();
ctx->getDeclKind() != Decl::TranslationUnit;
ctx = ctx->getLexicalParent())
{
if (auto ls = dyn_cast<LinkageSpecDecl>(ctx)) {
if (!ls->hasBraces()) {
return true;
}
if (auto prev = decl->getPreviousDecl()) {
return hasExternalLinkage(prev);
}
return !decl->isInAnonymousNamespace();
}
}
return true;
}
bool isSmartPointerType(const Expr* e)
{
// First check whether the object type as written is, or is derived from, std::unique_ptr or
// std::shared_ptr, in case the get member function is declared at a base class of that std
// type:
if (loplugin::isDerivedFrom(
e->IgnoreImpCasts()->getType()->getAsCXXRecordDecl(),
[](Decl const * decl) {
auto const dc = loplugin::DeclCheck(decl);
return dc.ClassOrStruct("unique_ptr").StdNamespace()
|| dc.ClassOrStruct("shared_ptr").StdNamespace();
}))
return true;
// Then check the object type coerced to the type of the get member function, in
// case the type-as-written is derived from one of these types (tools::SvRef is
// final, but the rest are not):
Fix loplugin:simplifypointertobool for libstdc++ std::shared_ptr ...where the get member function is defined on a std::__shared_ptr base class, so loplugin:simplifypointertobool used to miss those until now. (While e.g. using libc++ on macOS found those cases.) 366d08f2f6d4de922f6099c62bb81b49d89e0a68 "new loplugin:simplifypointertobool" was mistaken in breaking isSmartPointerType(const clang::Type* t) out of isSmartPointerType(const Expr* e); c874294ad9fb178df47c66875bfbdec466e39763 "Fix detection of std::unique_ptr/shared_ptr in loplugin:redundantpointerops" had introduced that indivisible two-step algorithm on purpose. The amount of additional hits (on Linux) apparently asked for turning loplugin:simplifypointertobool into a rewriting plugin. Which in turn showed that the naive adivce to just "drop the get()" is not sufficient in places that are not contextually converted to bool, as those places need to be wrapped in a bool(...) functional cast now. If the expression was already wrapped in parentheses, those could be reused as part of the functional cast, but implementing that showed that such cases are not yet found at all by the existing loplugin:simplifypointertobool. Lets leave that TODO for another commit. Besides the changes to compilerplugins/ itself, this change has been generated fully automatically with the rewriting plugin on Linux. Change-Id: I83107d6f634fc9ac232986f49044d7017df83e2a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94888 Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk> Reviewed-by: Stephan Bergmann <sbergman@redhat.com> Tested-by: Jenkins
2020-05-26 20:42:08 +02:00
auto const tc2 = loplugin::TypeCheck(e->getType());
if (tc2.ClassOrStruct("unique_ptr").StdNamespace()
|| tc2.ClassOrStruct("shared_ptr").StdNamespace()
|| tc2.Class("Reference").Namespace("uno").Namespace("star")
.Namespace("sun").Namespace("com").GlobalNamespace()
|| tc2.Class("Reference").Namespace("rtl").GlobalNamespace()
|| tc2.Class("SvRef").Namespace("tools").GlobalNamespace()
|| tc2.Class("WeakReference").Namespace("tools").GlobalNamespace()
|| tc2.Class("ScopedReadAccess").Namespace("Bitmap").GlobalNamespace()
|| tc2.Class("ScopedVclPtrInstance").GlobalNamespace()
|| tc2.Class("VclPtr").GlobalNamespace()
|| tc2.Class("ScopedVclPtr").GlobalNamespace()
|| tc2.Class("intrusive_ptr").Namespace("boost").GlobalNamespace())
{
return true;
}
return false;
}
} // namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */