2013-09-21 14:42:35 +01:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
2012-10-05 18:17:13 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2012-10-15 15:36:25 +02:00
|
|
|
#include "plugin.hxx"
|
2012-10-05 18:17:13 +02:00
|
|
|
|
2014-01-27 13:09:20 +01:00
|
|
|
#include <cassert>
|
2017-05-18 09:56:01 +02:00
|
|
|
#include <cstddef>
|
2016-09-22 08:50:43 +02:00
|
|
|
#include <string>
|
2014-01-27 13:09:20 +01:00
|
|
|
|
2012-10-15 15:36:25 +02:00
|
|
|
#include <clang/Basic/FileManager.h>
|
2013-08-14 16:39:11 +02:00
|
|
|
#include <clang/Lex/Lexer.h>
|
2012-10-05 18:17:13 +02:00
|
|
|
|
2020-01-27 15:00:41 +01:00
|
|
|
#include "config_clang.h"
|
|
|
|
|
2019-11-25 13:04:02 +01:00
|
|
|
#include "compat.hxx"
|
2013-02-02 16:35:47 +01:00
|
|
|
#include "pluginhandler.hxx"
|
2020-04-08 12:36:53 +02:00
|
|
|
#include "check.hxx"
|
2012-10-09 14:50:19 +02:00
|
|
|
|
2020-01-27 15:00:41 +01:00
|
|
|
#if CLANG_VERSION >= 110000
|
|
|
|
#include "clang/AST/ParentMapContext.h"
|
|
|
|
#endif
|
|
|
|
|
2013-02-02 18:34:12 +01:00
|
|
|
/*
|
|
|
|
Base classes for plugin actions.
|
|
|
|
*/
|
2012-10-05 18:17:13 +02:00
|
|
|
namespace loplugin
|
|
|
|
{
|
|
|
|
|
2017-11-14 13:55:17 +01:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
Expr const * skipImplicit(Expr const * expr) {
|
|
|
|
if (auto const e = dyn_cast<MaterializeTemporaryExpr>(expr)) {
|
2019-11-25 13:04:02 +01:00
|
|
|
expr = compat::getSubExpr(e)->IgnoreImpCasts();
|
2017-11-14 13:55:17 +01:00
|
|
|
}
|
|
|
|
if (auto const e = dyn_cast<CXXBindTemporaryExpr>(expr)) {
|
|
|
|
expr = e->getSubExpr();
|
|
|
|
}
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2017-11-15 17:41:22 +01:00
|
|
|
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:
|
2020-02-20 21:04:11 +01:00
|
|
|
case Stmt::CXXBindTemporaryExprClass:
|
2017-11-15 17:41:22 +01:00
|
|
|
case Stmt::ParenExprClass:
|
|
|
|
break;
|
2020-02-20 21:04:11 +01:00
|
|
|
case Stmt::CXXNullPtrLiteralExprClass:
|
|
|
|
return true;
|
2017-11-15 17:41:22 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-11-14 13:55:17 +01:00
|
|
|
}
|
|
|
|
|
2014-01-27 13:09:20 +01:00
|
|
|
Plugin::Plugin( const InstantiationData& data )
|
2014-02-20 19:47:01 +01:00
|
|
|
: compiler( data.compiler ), handler( data.handler ), name( data.name )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
|
|
|
}
|
2012-10-09 16:27:25 +02:00
|
|
|
|
2014-02-14 15:54:59 +01:00
|
|
|
DiagnosticBuilder Plugin::report( DiagnosticsEngine::Level level, StringRef message, SourceLocation loc ) const
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
return handler.report( level, name, message, compiler, loc );
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-09 16:27:25 +02:00
|
|
|
|
2017-11-07 11:19:30 +01:00
|
|
|
void normalizeDotDotInFilePath( std::string & s )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
|
|
|
for (std::string::size_type i = 0;;)
|
2016-09-27 10:00:03 +02:00
|
|
|
{
|
|
|
|
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);
|
2017-06-20 10:26:46 +02:00
|
|
|
if (j == std::string::npos)
|
|
|
|
{
|
2016-09-27 10:00:03 +02:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2016-09-27 10:00:03 +02:00
|
|
|
|
2019-03-07 14:31:17 +01:00
|
|
|
void Plugin::registerPlugin( Plugin* (*create)( const InstantiationData& ), const char* optionName,
|
|
|
|
bool isPPCallback, bool isSharedPlugin, bool byDefault )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2019-03-07 14:31:17 +01:00
|
|
|
PluginHandler::registerPlugin( create, optionName, isPPCallback, isSharedPlugin, byDefault );
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2013-02-02 17:45:18 +01:00
|
|
|
|
2017-11-10 13:40:12 +01:00
|
|
|
bool Plugin::evaluate(const Expr* expr, APSInt& x)
|
|
|
|
{
|
2018-11-27 12:56:12 +02:00
|
|
|
if (compat::EvaluateAsInt(expr, x, compiler.getASTContext()))
|
2017-11-10 13:40:12 +01:00
|
|
|
{
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-10-27 09:51:21 +02:00
|
|
|
const Stmt* Plugin::getParentStmt( const Stmt* stmt )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
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);
|
2017-10-27 10:15:33 +02:00
|
|
|
if ( parentsRange.begin() == parentsRange.end())
|
|
|
|
return nullptr;
|
|
|
|
return parentsRange.begin()->get<Stmt>();
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2013-06-19 23:55:47 +02:00
|
|
|
|
2017-10-27 09:51:21 +02:00
|
|
|
Stmt* Plugin::getParentStmt( Stmt* stmt )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
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);
|
2017-10-27 10:15:33 +02:00
|
|
|
if ( parentsRange.begin() == parentsRange.end())
|
|
|
|
return nullptr;
|
|
|
|
return const_cast<Stmt*>(parentsRange.begin()->get<Stmt>());
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2013-06-19 23:55:47 +02:00
|
|
|
|
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)
|
2020-03-06 14:40:40 +02:00
|
|
|
{
|
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);
|
2020-03-10 18:57:36 +01:00
|
|
|
auto it = parents.begin();
|
2020-03-06 14:40:40 +02:00
|
|
|
|
2020-03-10 18:57:36 +01:00
|
|
|
if (it == parents.end())
|
2020-03-06 14:40:40 +02:00
|
|
|
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);
|
2020-03-06 14:40:40 +02:00
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-10-27 09:51:21 +02:00
|
|
|
const FunctionDecl* Plugin::getParentFunctionDecl( const Stmt* stmt )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
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);
|
2016-06-28 14:52:54 +02:00
|
|
|
if (decl)
|
|
|
|
return static_cast<const FunctionDecl*>(decl->getNonClosureContext());
|
|
|
|
|
|
|
|
return nullptr;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2016-06-28 14:52:54 +02:00
|
|
|
|
2019-10-07 15:29:30 +02:00
|
|
|
StringRef Plugin::getFilenameOfLocation(SourceLocation spellingLocation) const
|
2018-05-29 11:28:07 +02:00
|
|
|
{
|
2018-09-10 15:31:24 +02:00
|
|
|
// prevent crashes when running the global-analysis plugins
|
|
|
|
if (!spellingLocation.isValid())
|
|
|
|
return "";
|
|
|
|
|
2018-05-29 11:28:07 +02:00
|
|
|
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;
|
2019-10-07 15:29:30 +02:00
|
|
|
return getFilenameOfLocation(spellingLocation);
|
2018-05-29 11:28:07 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-24 12:47:28 +02:00
|
|
|
|
2017-06-20 10:26:46 +02:00
|
|
|
bool Plugin::isInUnoIncludeFile(SourceLocation spellingLocation) const
|
|
|
|
{
|
2019-10-07 15:29:30 +02:00
|
|
|
StringRef name{ getFilenameOfLocation(spellingLocation) };
|
2016-06-29 09:15:05 +02:00
|
|
|
return compiler.getSourceManager().isInMainFile(spellingLocation)
|
2017-05-18 09:56:01 +02:00
|
|
|
? (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/"));
|
2014-06-24 12:47:28 +02:00
|
|
|
}
|
|
|
|
|
2017-06-20 10:26:46 +02:00
|
|
|
bool Plugin::isInUnoIncludeFile(const FunctionDecl* functionDecl) const
|
|
|
|
{
|
2016-10-18 08:15:21 +02:00
|
|
|
return isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(
|
|
|
|
functionDecl->getCanonicalDecl()->getNameInfo().getLoc()));
|
|
|
|
}
|
|
|
|
|
2013-08-14 18:43:01 +02:00
|
|
|
SourceLocation Plugin::locationAfterToken( SourceLocation location )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2013-08-14 18:43:01 +02:00
|
|
|
return Lexer::getLocForEndOfToken( location, 0, compiler.getSourceManager(), compiler.getLangOpts());
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2013-08-14 18:43:01 +02:00
|
|
|
|
2017-06-19 13:44:14 +02:00
|
|
|
bool Plugin::isUnitTestMode()
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2017-06-19 13:44:14 +02:00
|
|
|
return PluginHandler::isUnitTestMode();
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2017-06-19 13:44:14 +02:00
|
|
|
|
2017-07-21 15:01:44 +02:00
|
|
|
bool Plugin::containsPreprocessingConditionalInclusion(SourceRange range)
|
|
|
|
{
|
2019-10-29 22:53:33 +01:00
|
|
|
// 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:
|
2017-07-21 15:01:44 +02:00
|
|
|
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)))
|
|
|
|
{
|
2019-10-29 22:53:33 +01:00
|
|
|
if (isDebugMode()) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Fatal,
|
|
|
|
("unexpected broken range for Plugin::containsPreprocessingConditionalInclusion,"
|
|
|
|
" case 1"),
|
|
|
|
range.getBegin())
|
|
|
|
<< range;
|
|
|
|
}
|
|
|
|
// Conservatively assume "yes" if lexing fails:
|
2017-07-21 15:01:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
auto hash = false;
|
|
|
|
for (auto loc = begin;;) {
|
|
|
|
Token tok;
|
|
|
|
if (Lexer::getRawToken(
|
|
|
|
loc, tok, compiler.getSourceManager(),
|
|
|
|
compiler.getLangOpts(), true))
|
|
|
|
{
|
2019-10-29 22:53:33 +01:00
|
|
|
if (isDebugMode()) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Fatal,
|
|
|
|
("unexpected broken range for"
|
|
|
|
" Plugin::containsPreprocessingConditionalInclusion, case 2"),
|
|
|
|
loc)
|
|
|
|
<< range;
|
|
|
|
}
|
|
|
|
// Conservatively assume "yes" if lexing fails:
|
2017-07-21 15:01:44 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2019-10-29 22:53:33 +01:00
|
|
|
if (loc == end) {
|
2017-07-21 15:01:44 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
hash = tok.is(tok::hash) && tok.isAtStartOfLine();
|
|
|
|
loc = loc.getLocWithOffset(
|
|
|
|
std::max<unsigned>(
|
|
|
|
Lexer::MeasureTokenLength(
|
|
|
|
loc, compiler.getSourceManager(),
|
|
|
|
compiler.getLangOpts()),
|
|
|
|
1));
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-11-10 13:40:12 +01:00
|
|
|
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;
|
|
|
|
}
|
2017-11-14 13:55:17 +01:00
|
|
|
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)) {
|
2017-11-10 13:40:12 +01:00
|
|
|
return lit1->getBytes() == lit2->getBytes()
|
|
|
|
? IdenticalDefaultArgumentsResult::Yes
|
|
|
|
: IdenticalDefaultArgumentsResult::No;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// catch params with defaults like "= OUString()"
|
2017-11-14 19:17:07 +01:00
|
|
|
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())
|
2017-11-14 13:55:17 +01:00
|
|
|
{
|
2017-11-14 19:17:07 +01:00
|
|
|
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;
|
2017-11-14 13:55:17 +01:00
|
|
|
}
|
2017-11-16 15:12:05 +01:00
|
|
|
break;
|
2017-11-10 13:40:12 +01:00
|
|
|
}
|
2017-11-15 17:41:22 +01:00
|
|
|
// 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;
|
|
|
|
}
|
2017-12-08 16:31:34 +01:00
|
|
|
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();
|
|
|
|
}
|
2017-11-10 13:40:12 +01:00
|
|
|
return IdenticalDefaultArgumentsResult::Maybe;
|
|
|
|
}
|
|
|
|
|
2014-01-27 13:09:20 +01:00
|
|
|
RewritePlugin::RewritePlugin( const InstantiationData& data )
|
|
|
|
: Plugin( data )
|
|
|
|
, rewriter( data.rewriter )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::insertText( SourceLocation Loc, StringRef Str, bool InsertAfter, bool indentNewLines )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
2017-05-18 14:18:58 +02:00
|
|
|
if (wouldRewriteWorkdir(Loc))
|
|
|
|
return false;
|
2018-02-12 10:23:59 +02:00
|
|
|
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;
|
|
|
|
}
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->InsertText( Loc, Str, InsertAfter, indentNewLines ))
|
2012-10-15 17:34:13 +02:00
|
|
|
return reportEditFailure( Loc );
|
2018-02-12 10:23:59 +02:00
|
|
|
handler.addSourceModification(Range);
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::insertTextAfter( SourceLocation Loc, StringRef Str )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
2017-05-18 14:18:58 +02:00
|
|
|
if (wouldRewriteWorkdir(Loc))
|
|
|
|
return false;
|
2018-02-12 10:23:59 +02:00
|
|
|
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;
|
|
|
|
}
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->InsertTextAfter( Loc, Str ))
|
2012-10-15 17:34:13 +02:00
|
|
|
return reportEditFailure( Loc );
|
2018-02-12 10:23:59 +02:00
|
|
|
handler.addSourceModification(Range);
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::insertTextAfterToken( SourceLocation Loc, StringRef Str )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
2017-05-18 14:18:58 +02:00
|
|
|
if (wouldRewriteWorkdir(Loc))
|
|
|
|
return false;
|
2018-02-12 10:23:59 +02:00
|
|
|
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;
|
|
|
|
}
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->InsertTextAfterToken( Loc, Str ))
|
2012-10-15 17:34:13 +02:00
|
|
|
return reportEditFailure( Loc );
|
2018-02-12 10:23:59 +02:00
|
|
|
handler.addSourceModification(Range);
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::insertTextBefore( SourceLocation Loc, StringRef Str )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
2017-05-18 14:18:58 +02:00
|
|
|
if (wouldRewriteWorkdir(Loc))
|
|
|
|
return false;
|
2018-02-12 10:23:59 +02:00
|
|
|
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;
|
|
|
|
}
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->InsertTextBefore( Loc, Str ))
|
2012-10-15 17:34:13 +02:00
|
|
|
return reportEditFailure( Loc );
|
2018-02-12 10:23:59 +02:00
|
|
|
handler.addSourceModification(Range);
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::removeText( SourceLocation Start, unsigned Length, RewriteOptions opts )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2013-08-14 16:39:11 +02:00
|
|
|
CharSourceRange range( SourceRange( Start, Start.getLocWithOffset( Length )), false );
|
|
|
|
return removeText( range, opts );
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
2012-12-20 23:08:52 +01:00
|
|
|
bool RewritePlugin::removeText( SourceRange range, RewriteOptions opts )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2013-08-14 16:39:11 +02:00
|
|
|
return removeText( CharSourceRange( range, true ), opts );
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2013-08-14 16:39:11 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::removeText( CharSourceRange range, RewriteOptions opts )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
2017-05-18 14:18:58 +02:00
|
|
|
if (wouldRewriteWorkdir(range.getBegin()))
|
|
|
|
return false;
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->getRangeSize( range, opts ) == -1 )
|
2013-08-20 19:42:16 +02:00
|
|
|
return reportEditFailure( range.getBegin());
|
2018-02-09 15:28:41 +02:00
|
|
|
if( !handler.checkOverlap( range.getAsRange() ) )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2013-05-06 16:41:27 +02:00
|
|
|
report( DiagnosticsEngine::Warning, "double code removal, possible plugin error", range.getBegin());
|
2018-02-12 10:23:59 +02:00
|
|
|
return false;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2013-08-14 16:39:11 +02:00
|
|
|
if( opts.flags & RemoveWholeStatement || opts.flags & RemoveAllWhitespace )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2013-08-14 16:39:11 +02:00
|
|
|
if( !adjustRangeForOptions( &range, opts ))
|
2012-12-20 23:08:52 +01:00
|
|
|
return reportEditFailure( range.getBegin());
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->RemoveText( range, opts ))
|
2012-10-15 17:34:13 +02:00
|
|
|
return reportEditFailure( range.getBegin());
|
2018-02-09 15:28:41 +02:00
|
|
|
handler.addSourceModification(range.getAsRange());
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
2013-08-14 16:39:11 +02:00
|
|
|
bool RewritePlugin::adjustRangeForOptions( CharSourceRange* range, RewriteOptions opts )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
|
|
|
SourceManager& SM = rewriter->getSourceMgr();
|
2012-12-20 23:08:52 +01:00
|
|
|
SourceLocation fileStartLoc = SM.getLocForStartOfFile( SM.getFileID( range->getBegin()));
|
|
|
|
if( fileStartLoc.isInvalid())
|
|
|
|
return false;
|
2015-09-25 11:41:53 +01:00
|
|
|
bool isInvalid = false;
|
|
|
|
const char* fileBuf = SM.getCharacterData( fileStartLoc, &isInvalid );
|
|
|
|
if( isInvalid )
|
2012-12-20 23:08:52 +01:00
|
|
|
return false;
|
2015-09-25 11:41:53 +01:00
|
|
|
const char* startBuf = SM.getCharacterData( range->getBegin(), &isInvalid );
|
|
|
|
if( isInvalid )
|
2012-12-20 23:08:52 +01:00
|
|
|
return false;
|
2013-08-14 16:39:11 +02:00
|
|
|
SourceLocation locationEnd = range->getEnd();
|
|
|
|
if( range->isTokenRange())
|
2013-08-14 18:43:01 +02:00
|
|
|
locationEnd = locationAfterToken( locationEnd );
|
2015-09-25 11:41:53 +01:00
|
|
|
const char* endBuf = SM.getCharacterData( locationEnd, &isInvalid );
|
|
|
|
if( isInvalid )
|
2012-12-20 23:08:52 +01:00
|
|
|
return false;
|
2013-08-14 16:39:11 +02:00
|
|
|
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 )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2013-08-14 16:39:11 +02:00
|
|
|
if( *endPos == ';' )
|
|
|
|
++endPos;
|
|
|
|
else
|
|
|
|
return false;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2013-08-14 16:39:11 +02:00
|
|
|
*range = CharSourceRange( SourceRange( range->getBegin().getLocWithOffset( startPos - startBuf + 1 ),
|
|
|
|
locationEnd.getLocWithOffset( endPos - endBuf )), false );
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::replaceText( SourceLocation Start, unsigned OrigLength, StringRef NewStr )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
2017-05-18 14:18:58 +02:00
|
|
|
if (wouldRewriteWorkdir(Start))
|
|
|
|
return false;
|
2018-02-09 15:28:41 +02:00
|
|
|
SourceRange Range(Start, Start.getLocWithOffset(std::max<size_t>(OrigLength, NewStr.size())));
|
|
|
|
if( OrigLength != 0 && !handler.checkOverlap( Range ) )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2018-02-15 14:01:04 +02:00
|
|
|
report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", Start );
|
2018-02-12 10:23:59 +02:00
|
|
|
return false;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->ReplaceText( Start, OrigLength, NewStr ))
|
2012-10-15 17:34:13 +02:00
|
|
|
return reportEditFailure( Start );
|
2018-02-09 15:28:41 +02:00
|
|
|
handler.addSourceModification(Range);
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::replaceText( SourceRange range, StringRef NewStr )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
2017-05-18 14:18:58 +02:00
|
|
|
if (wouldRewriteWorkdir(range.getBegin()))
|
|
|
|
return false;
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->getRangeSize( range ) == -1 )
|
2013-08-20 19:42:16 +02:00
|
|
|
return reportEditFailure( range.getBegin());
|
2018-02-09 15:28:41 +02:00
|
|
|
if( !handler.checkOverlap( range ) )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2018-02-15 14:01:04 +02:00
|
|
|
report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", range.getBegin());
|
2018-02-12 10:23:59 +02:00
|
|
|
return false;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->ReplaceText( range, NewStr ))
|
2012-10-15 17:34:13 +02:00
|
|
|
return reportEditFailure( range.getBegin());
|
2018-02-09 15:28:41 +02:00
|
|
|
handler.addSourceModification(range);
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
|
|
|
bool RewritePlugin::replaceText( SourceRange range, SourceRange replacementRange )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2014-01-27 13:09:20 +01:00
|
|
|
assert( rewriter );
|
2017-05-18 14:18:58 +02:00
|
|
|
if (wouldRewriteWorkdir(range.getBegin()))
|
|
|
|
return false;
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->getRangeSize( range ) == -1 )
|
2013-08-20 19:42:16 +02:00
|
|
|
return reportEditFailure( range.getBegin());
|
2018-02-09 15:28:41 +02:00
|
|
|
if( !handler.checkOverlap( range ) )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2018-02-15 14:01:04 +02:00
|
|
|
report( DiagnosticsEngine::Warning, "overlapping code replacement, possible plugin error", range.getBegin());
|
2018-02-12 10:23:59 +02:00
|
|
|
return false;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2014-01-27 13:09:20 +01:00
|
|
|
if( rewriter->ReplaceText( range, replacementRange ))
|
2012-10-15 17:34:13 +02:00
|
|
|
return reportEditFailure( range.getBegin());
|
2018-02-09 15:28:41 +02:00
|
|
|
handler.addSourceModification(range);
|
2012-10-15 17:34:13 +02:00
|
|
|
return true;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
2017-06-20 10:26:46 +02:00
|
|
|
bool RewritePlugin::wouldRewriteWorkdir(SourceLocation loc)
|
|
|
|
{
|
2017-05-18 14:18:58 +02:00
|
|
|
if (loc.isInvalid() || loc.isMacroID()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return
|
2019-10-07 15:29:30 +02:00
|
|
|
getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(loc))
|
2017-05-18 14:18:58 +02:00
|
|
|
.startswith(WORKDIR "/");
|
|
|
|
}
|
|
|
|
|
2012-10-15 17:34:13 +02:00
|
|
|
bool RewritePlugin::reportEditFailure( SourceLocation loc )
|
2017-06-20 10:26:46 +02:00
|
|
|
{
|
2013-02-02 19:38:56 +01:00
|
|
|
report( DiagnosticsEngine::Warning, "cannot perform source modification (macro expansion involved?)", loc );
|
2012-10-15 17:34:13 +02:00
|
|
|
return false;
|
2017-06-20 10:26:46 +02:00
|
|
|
}
|
2012-10-15 17:34:13 +02:00
|
|
|
|
2017-05-18 09:56:01 +02:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
template<typename Fn> bool checkPathname(
|
|
|
|
StringRef pathname, StringRef against, Fn check)
|
|
|
|
{
|
|
|
|
if (check(pathname, against)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#if defined _WIN32
|
2017-06-20 10:26:46 +02:00
|
|
|
for (std::size_t n = 0;;)
|
|
|
|
{
|
2017-05-18 09:56:01 +02:00
|
|
|
std::size_t n1 = pathname.find('\\', n);
|
2017-10-27 15:57:48 +02:00
|
|
|
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;
|
2017-05-18 09:56:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-06-20 10:26:46 +02:00
|
|
|
bool hasPathnamePrefix(StringRef pathname, StringRef prefix)
|
|
|
|
{
|
2017-05-18 09:56:01 +02:00
|
|
|
return checkPathname(
|
|
|
|
pathname, prefix,
|
|
|
|
[](StringRef p, StringRef a) { return p.startswith(a); });
|
|
|
|
}
|
|
|
|
|
2017-06-20 10:26:46 +02:00
|
|
|
bool isSamePathname(StringRef pathname, StringRef other)
|
|
|
|
{
|
2017-05-18 09:56:01 +02:00
|
|
|
return checkPathname(
|
2018-03-23 10:49:57 +01:00
|
|
|
pathname, other, [](StringRef p, StringRef a) { return p == a; });
|
2017-05-18 09:56:01 +02:00
|
|
|
}
|
|
|
|
|
2019-07-17 15:06:43 +02:00
|
|
|
bool hasCLanguageLinkageType(FunctionDecl const * decl) {
|
|
|
|
assert(decl != nullptr);
|
|
|
|
if (decl->isExternC()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (decl->isInExternCContext()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-27 11:06:41 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-02-13 12:12:59 +02:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2020-04-08 12:36:53 +02:00
|
|
|
bool isSmartPointerType(const Expr* e)
|
|
|
|
{
|
2020-05-27 16:00:08 +02:00
|
|
|
// 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();
|
|
|
|
}))
|
2020-04-08 12:36:53 +02:00
|
|
|
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
|
2020-05-27 16:00:08 +02:00
|
|
|
// final, but the rest are not):
|
2020-05-26 20:42:08 +02:00
|
|
|
auto const tc2 = loplugin::TypeCheck(e->getType());
|
2020-04-08 12:36:53 +02:00
|
|
|
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()
|
2020-06-05 08:57:32 +02:00
|
|
|
|| tc2.Class("ScopedVclPtr").GlobalNamespace()
|
|
|
|
|| tc2.Class("intrusive_ptr").Namespace("boost").GlobalNamespace())
|
2020-04-08 12:36:53 +02:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-27 11:06:41 +02:00
|
|
|
|
2012-10-05 18:17:13 +02:00
|
|
|
} // namespace
|
2013-09-21 14:42:35 +01:00
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|