d2c5490210
> compilerplugins/clang/casttovoid.cxx:452:18: error: 'endswith' is deprecated: Use ends_with instead [-Werror,-Wdeprecated-declarations] > .endswith(".h")); > ^~~~~~~~ > ends_with > ~/llvm/inst/include/llvm/ADT/StringRef.h:276:19: note: 'endswith' has been explicitly marked deprecated here > [[nodiscard]] LLVM_DEPRECATED( > ^ etc. after <5ac12951b4
> "[ADT] Deprecate StringRef::{starts,ends}with (#75491)" on Clang 18 trunk, where <1b97645e56
> "[ADT] Introduce StringRef::{starts,ends}_width{,_insensitive}" had been added towards Clang 16 Change-Id: Icb3e43b7d6be6f877815285913d846f766eddebf Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160919 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
300 lines
9.8 KiB
C++
300 lines
9.8 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/FrontendAction.h"
|
|
#include "clang/Tooling/Tooling.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
|
|
#include <cassert>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <fstream>
|
|
#include <set>
|
|
|
|
#include "config_clang.h"
|
|
#include "../check.hxx"
|
|
#include "../check.cxx"
|
|
#include "../compat.hxx"
|
|
|
|
using namespace clang;
|
|
using namespace llvm;
|
|
|
|
using namespace loplugin;
|
|
|
|
// Info about a Traverse* function in a plugin.
|
|
struct TraverseFunctionInfo
|
|
{
|
|
std::string name;
|
|
std::string argument;
|
|
bool hasPre = false;
|
|
bool hasPost = false;
|
|
};
|
|
|
|
struct TraverseFunctionInfoLess
|
|
{
|
|
bool operator()( const TraverseFunctionInfo& l, const TraverseFunctionInfo& r ) const
|
|
{
|
|
return l.name < r.name;
|
|
}
|
|
};
|
|
|
|
static std::set< TraverseFunctionInfo, TraverseFunctionInfoLess > traverseFunctions;
|
|
|
|
class CheckFileVisitor
|
|
: public RecursiveASTVisitor< CheckFileVisitor >
|
|
{
|
|
public:
|
|
void setContext(ASTContext const& context) { context_ = &context; }
|
|
|
|
bool VisitCXXRecordDecl(CXXRecordDecl *Declaration);
|
|
|
|
bool TraverseNamespaceDecl(NamespaceDecl * decl)
|
|
{
|
|
// Skip non-LO namespaces the same way FilteringPlugin does.
|
|
if( !ContextCheck( decl ).Namespace( "loplugin" ).GlobalNamespace()
|
|
&& !ContextCheck( decl ).AnonymousNamespace())
|
|
{
|
|
return true;
|
|
}
|
|
return RecursiveASTVisitor<CheckFileVisitor>::TraverseNamespaceDecl(decl);
|
|
}
|
|
|
|
private:
|
|
ASTContext const* context_ = nullptr;
|
|
|
|
QualType unqualifyPointeeType(QualType type)
|
|
{
|
|
assert(context_ != nullptr);
|
|
if (auto const t = type->getAs<clang::PointerType>())
|
|
{
|
|
return context_->getQualifiedType(
|
|
context_->getPointerType(t->getPointeeType().getUnqualifiedType()),
|
|
type.getQualifiers());
|
|
}
|
|
return type;
|
|
}
|
|
};
|
|
|
|
static bool inheritsPluginClassCheck( const Decl* decl )
|
|
{
|
|
return bool( DeclCheck( decl ).Class( "FilteringPlugin" ).Namespace( "loplugin" ).GlobalNamespace())
|
|
|| bool( DeclCheck( decl ).Class( "FilteringRewritePlugin" ).Namespace( "loplugin" ).GlobalNamespace());
|
|
}
|
|
|
|
static TraverseFunctionInfo findOrCreateTraverseFunctionInfo( StringRef name )
|
|
{
|
|
TraverseFunctionInfo info;
|
|
info.name = name.str();
|
|
auto foundInfo = traverseFunctions.find( info );
|
|
if( foundInfo != traverseFunctions.end())
|
|
{
|
|
info = std::move( *foundInfo );
|
|
traverseFunctions.erase( foundInfo );
|
|
}
|
|
return info;
|
|
}
|
|
|
|
static bool foundSomething;
|
|
|
|
bool CheckFileVisitor::VisitCXXRecordDecl( CXXRecordDecl* decl )
|
|
{
|
|
if( !isDerivedFrom( decl, inheritsPluginClassCheck ))
|
|
return true;
|
|
|
|
if( decl->getName() == "FilteringPlugin" || decl->getName() == "FilteringRewritePlugin" )
|
|
return true;
|
|
|
|
std::cout << "# This file is autogenerated. Do not modify." << std::endl;
|
|
std::cout << "# Generated by compilerplugins/clang/sharedvisitor/analyzer.cxx ." << std::endl;
|
|
std::cout << "InfoVersion:1" << std::endl;
|
|
std::cout << "ClassName:" << decl->getName().str() << std::endl;
|
|
traverseFunctions.clear();
|
|
for( const CXXMethodDecl* method : decl->methods())
|
|
{
|
|
if( !method->getDeclName().isIdentifier())
|
|
continue;
|
|
if( method->isStatic() || method->getAccess() != AS_public )
|
|
continue;
|
|
if( compat::starts_with(method->getName(), "Visit" ))
|
|
{
|
|
if( method->getNumParams() == 1 )
|
|
{
|
|
std::cout << "VisitFunctionStart" << std::endl;
|
|
std::cout << "VisitFunctionName:" << method->getName().str() << std::endl;
|
|
std::cout << "VisitFunctionArgument:"
|
|
<< unqualifyPointeeType(
|
|
method->getParamDecl( 0 )->getTypeSourceInfo()->getType()).getAsString()
|
|
<< std::endl;
|
|
std::cout << "VisitFunctionEnd" << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Unhandled Visit* function: " << decl->getName().str()
|
|
<< "::" << method->getName().str() << std::endl;
|
|
abort();
|
|
}
|
|
}
|
|
else if( compat::starts_with(method->getName(), "Traverse" ))
|
|
{
|
|
if( method->getNumParams() == 1 )
|
|
{
|
|
TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( method->getName());
|
|
traverseInfo.argument = method->getParamDecl( 0 )->getTypeSourceInfo()->getType().getAsString();
|
|
traverseFunctions.insert( std::move( traverseInfo ));
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Unhandled Traverse* function: " << decl->getName().str()
|
|
<< "::" << method->getName().str() << std::endl;
|
|
abort();
|
|
}
|
|
}
|
|
else if( compat::starts_with(method->getName(), "PreTraverse" ))
|
|
{
|
|
TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( method->getName().substr( 3 ));
|
|
traverseInfo.hasPre = true;
|
|
traverseFunctions.insert( std::move( traverseInfo ));
|
|
}
|
|
else if( compat::starts_with(method->getName(), "PostTraverse" ))
|
|
{
|
|
TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( method->getName().substr( 4 ));
|
|
traverseInfo.hasPost = true;
|
|
traverseFunctions.insert( std::move( traverseInfo ));
|
|
}
|
|
else if( method->getName() == "shouldVisitTemplateInstantiations" )
|
|
std::cout << "ShouldVisitTemplateInstantiations:1" << std::endl;
|
|
else if (method->getName() == "shouldVisitImplicitCode")
|
|
std::cout << "ShouldVisitImplicitCode:1" << std::endl;
|
|
else if( compat::starts_with(method->getName(), "WalkUp" ))
|
|
{
|
|
std::cerr << "WalkUp function not supported for shared visitor: " << decl->getName().str()
|
|
<< "::" << method->getName().str() << std::endl;
|
|
abort();
|
|
}
|
|
}
|
|
|
|
for( const auto& traverseFunction : traverseFunctions )
|
|
{
|
|
std::cout << "TraverseFunctionStart" << std::endl;
|
|
std::cout << "TraverseFunctionName:" << traverseFunction.name << std::endl;
|
|
std::cout << "TraverseFunctionArgument:" << traverseFunction.argument << std::endl;
|
|
std::cout << "TraverseFunctionHasPre:" << traverseFunction.hasPre << std::endl;
|
|
std::cout << "TraverseFunctionHasPost:" << traverseFunction.hasPost << std::endl;
|
|
std::cout << "TraverseFunctionEnd" << std::endl;
|
|
}
|
|
|
|
std::cout << "InfoEnd" << std::endl;
|
|
foundSomething = true;
|
|
return true;
|
|
}
|
|
|
|
class FindNamedClassConsumer
|
|
: public ASTConsumer
|
|
{
|
|
public:
|
|
void Initialize(ASTContext& context) override
|
|
{
|
|
visitor.setContext(context);
|
|
}
|
|
virtual void HandleTranslationUnit(ASTContext& context) override
|
|
{
|
|
visitor.TraverseDecl( context.getTranslationUnitDecl());
|
|
}
|
|
private:
|
|
CheckFileVisitor visitor;
|
|
};
|
|
|
|
class FindNamedClassAction
|
|
: public ASTFrontendAction
|
|
{
|
|
public:
|
|
virtual std::unique_ptr<ASTConsumer> CreateASTConsumer( CompilerInstance&, StringRef ) override
|
|
{
|
|
return std::unique_ptr<ASTConsumer>( new FindNamedClassConsumer );
|
|
}
|
|
};
|
|
|
|
|
|
std::string readSourceFile( const char* filename )
|
|
{
|
|
std::string contents;
|
|
std::ifstream stream( filename );
|
|
if( !stream )
|
|
{
|
|
std::cerr << "Failed to open: " << filename << std::endl;
|
|
exit( 1 );
|
|
}
|
|
std::string line;
|
|
bool hasIfdef = false;
|
|
while( getline( stream, line ))
|
|
{
|
|
// TODO add checks that it's e.g. not "#ifdef" ?
|
|
if( line.find( "#ifndef LO_CLANG_SHARED_PLUGINS" ) == 0 )
|
|
hasIfdef = true;
|
|
contents += line;
|
|
contents += '\n';
|
|
}
|
|
if( stream.eof() && hasIfdef )
|
|
return contents;
|
|
return "";
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
std::vector< std::string > args;
|
|
int i = 1;
|
|
for( ; i < argc; ++ i )
|
|
{
|
|
constexpr std::size_t prefixlen = 5; // strlen("-arg=");
|
|
if (std::strncmp(argv[i], "-arg=", prefixlen) != 0)
|
|
{
|
|
break;
|
|
}
|
|
args.push_back(argv[i] + prefixlen);
|
|
}
|
|
SmallVector< StringRef, 20 > clangflags;
|
|
SplitString( CLANGFLAGS, clangflags );
|
|
for (auto const & i: clangflags) {
|
|
args.push_back(i.str());
|
|
}
|
|
args.insert(
|
|
args.end(),
|
|
{ // These must match LO_CLANG_ANALYZER_PCH_CXXFLAGS in Makefile-clang.mk .
|
|
"-I" BUILDDIR "/config_host" // plugin sources use e.g. config_global.h
|
|
#if LO_CLANG_USE_ANALYZER_PCH
|
|
,
|
|
"-include-pch", // use PCH with Clang headers to speed up parsing/analysing
|
|
BUILDDIR "/compilerplugins/clang/sharedvisitor/clang.pch"
|
|
#endif
|
|
});
|
|
for( ; i < argc; ++ i )
|
|
{
|
|
std::string contents = readSourceFile(argv[i]);
|
|
if( contents.empty())
|
|
continue;
|
|
foundSomething = false;
|
|
if( !tooling::runToolOnCodeWithArgs( std::unique_ptr<FindNamedClassAction>(new FindNamedClassAction), contents, args, argv[ i ] ))
|
|
{
|
|
std::cerr << "Failed to analyze: " << argv[ i ] << std::endl;
|
|
return 2;
|
|
}
|
|
if( !foundSomething )
|
|
{
|
|
// there's #ifndef LO_CLANG_SHARED_PLUGINS in the source, but no class matched
|
|
std::cerr << "Failed to find code: " << argv[ i ] << std::endl;
|
|
return 2;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|