1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include "clang/AST/ASTConsumer.h"
11 #include "clang/AST/RecursiveASTVisitor.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Frontend/FrontendAction.h"
14 #include "clang/Tooling/Tooling.h"
15 #include "llvm/ADT/StringExtras.h"
25 #include "config_clang.h"
26 #include "../check.hxx"
27 #include "../check.cxx"
29 using namespace clang
;
32 using namespace loplugin
;
34 // Info about a Traverse* function in a plugin.
35 struct TraverseFunctionInfo
43 struct TraverseFunctionInfoLess
45 bool operator()( const TraverseFunctionInfo
& l
, const TraverseFunctionInfo
& r
) const
47 return l
.name
< r
.name
;
51 static std::set
< TraverseFunctionInfo
, TraverseFunctionInfoLess
> traverseFunctions
;
53 class CheckFileVisitor
54 : public RecursiveASTVisitor
< CheckFileVisitor
>
57 void setContext(ASTContext
const& context
) { context_
= &context
; }
59 bool VisitCXXRecordDecl(CXXRecordDecl
*Declaration
);
61 bool TraverseNamespaceDecl(NamespaceDecl
* decl
)
63 // Skip non-LO namespaces the same way FilteringPlugin does.
64 if( !ContextCheck( decl
).Namespace( "loplugin" ).GlobalNamespace()
65 && !ContextCheck( decl
).AnonymousNamespace())
69 return RecursiveASTVisitor
<CheckFileVisitor
>::TraverseNamespaceDecl(decl
);
73 ASTContext
const* context_
= nullptr;
75 QualType
unqualifyPointeeType(QualType type
)
77 assert(context_
!= nullptr);
78 if (auto const t
= type
->getAs
<clang::PointerType
>())
80 return context_
->getQualifiedType(
81 context_
->getPointerType(t
->getPointeeType().getUnqualifiedType()),
82 type
.getQualifiers());
88 static bool inheritsPluginClassCheck( const Decl
* decl
)
90 return bool( DeclCheck( decl
).Class( "FilteringPlugin" ).Namespace( "loplugin" ).GlobalNamespace())
91 || bool( DeclCheck( decl
).Class( "FilteringRewritePlugin" ).Namespace( "loplugin" ).GlobalNamespace());
94 static TraverseFunctionInfo
findOrCreateTraverseFunctionInfo( StringRef name
)
96 TraverseFunctionInfo info
;
97 info
.name
= name
.str();
98 auto foundInfo
= traverseFunctions
.find( info
);
99 if( foundInfo
!= traverseFunctions
.end())
101 info
= std::move( *foundInfo
);
102 traverseFunctions
.erase( foundInfo
);
107 static bool foundSomething
;
109 bool CheckFileVisitor::VisitCXXRecordDecl( CXXRecordDecl
* decl
)
111 if( !isDerivedFrom( decl
, inheritsPluginClassCheck
))
114 if( decl
->getName() == "FilteringPlugin" || decl
->getName() == "FilteringRewritePlugin" )
117 std::cout
<< "# This file is autogenerated. Do not modify." << std::endl
;
118 std::cout
<< "# Generated by compilerplugins/clang/sharedvisitor/analyzer.cxx ." << std::endl
;
119 std::cout
<< "InfoVersion:1" << std::endl
;
120 std::cout
<< "ClassName:" << decl
->getName().str() << std::endl
;
121 traverseFunctions
.clear();
122 for( const CXXMethodDecl
* method
: decl
->methods())
124 if( !method
->getDeclName().isIdentifier())
126 if( method
->isStatic() || method
->getAccess() != AS_public
)
128 if( method
->getName().startswith( "Visit" ))
130 if( method
->getNumParams() == 1 )
132 std::cout
<< "VisitFunctionStart" << std::endl
;
133 std::cout
<< "VisitFunctionName:" << method
->getName().str() << std::endl
;
134 std::cout
<< "VisitFunctionArgument:"
135 << unqualifyPointeeType(
136 method
->getParamDecl( 0 )->getTypeSourceInfo()->getType()).getAsString()
138 std::cout
<< "VisitFunctionEnd" << std::endl
;
142 std::cerr
<< "Unhandled Visit* function: " << decl
->getName().str()
143 << "::" << method
->getName().str() << std::endl
;
147 else if( method
->getName().startswith( "Traverse" ))
149 if( method
->getNumParams() == 1 )
151 TraverseFunctionInfo traverseInfo
= findOrCreateTraverseFunctionInfo( method
->getName());
152 traverseInfo
.argument
= method
->getParamDecl( 0 )->getTypeSourceInfo()->getType().getAsString();
153 traverseFunctions
.insert( std::move( traverseInfo
));
157 std::cerr
<< "Unhandled Traverse* function: " << decl
->getName().str()
158 << "::" << method
->getName().str() << std::endl
;
162 else if( method
->getName().startswith( "PreTraverse" ))
164 TraverseFunctionInfo traverseInfo
= findOrCreateTraverseFunctionInfo( method
->getName().substr( 3 ));
165 traverseInfo
.hasPre
= true;
166 traverseFunctions
.insert( std::move( traverseInfo
));
168 else if( method
->getName().startswith( "PostTraverse" ))
170 TraverseFunctionInfo traverseInfo
= findOrCreateTraverseFunctionInfo( method
->getName().substr( 4 ));
171 traverseInfo
.hasPost
= true;
172 traverseFunctions
.insert( std::move( traverseInfo
));
174 else if( method
->getName() == "shouldVisitTemplateInstantiations" )
175 std::cout
<< "ShouldVisitTemplateInstantiations:1" << std::endl
;
176 else if (method
->getName() == "shouldVisitImplicitCode")
177 std::cout
<< "ShouldVisitImplicitCode:1" << std::endl
;
178 else if( method
->getName().startswith( "WalkUp" ))
180 std::cerr
<< "WalkUp function not supported for shared visitor: " << decl
->getName().str()
181 << "::" << method
->getName().str() << std::endl
;
186 for( const auto& traverseFunction
: traverseFunctions
)
188 std::cout
<< "TraverseFunctionStart" << std::endl
;
189 std::cout
<< "TraverseFunctionName:" << traverseFunction
.name
<< std::endl
;
190 std::cout
<< "TraverseFunctionArgument:" << traverseFunction
.argument
<< std::endl
;
191 std::cout
<< "TraverseFunctionHasPre:" << traverseFunction
.hasPre
<< std::endl
;
192 std::cout
<< "TraverseFunctionHasPost:" << traverseFunction
.hasPost
<< std::endl
;
193 std::cout
<< "TraverseFunctionEnd" << std::endl
;
196 std::cout
<< "InfoEnd" << std::endl
;
197 foundSomething
= true;
201 class FindNamedClassConsumer
205 void Initialize(ASTContext
& context
) override
207 visitor
.setContext(context
);
209 virtual void HandleTranslationUnit(ASTContext
& context
) override
211 visitor
.TraverseDecl( context
.getTranslationUnitDecl());
214 CheckFileVisitor visitor
;
217 class FindNamedClassAction
218 : public ASTFrontendAction
221 virtual std::unique_ptr
<ASTConsumer
> CreateASTConsumer( CompilerInstance
&, StringRef
) override
223 return std::unique_ptr
<ASTConsumer
>( new FindNamedClassConsumer
);
228 std::string
readSourceFile( const char* filename
)
230 std::string contents
;
231 std::ifstream
stream( filename
);
234 std::cerr
<< "Failed to open: " << filename
<< std::endl
;
238 bool hasIfdef
= false;
239 while( getline( stream
, line
))
241 // TODO add checks that it's e.g. not "#ifdef" ?
242 if( line
.find( "#ifndef LO_CLANG_SHARED_PLUGINS" ) == 0 )
247 if( stream
.eof() && hasIfdef
)
252 int main(int argc
, char** argv
)
254 std::vector
< std::string
> args
;
256 for( ; i
< argc
; ++ i
)
258 constexpr std::size_t prefixlen
= 5; // strlen("-arg=");
259 if (std::strncmp(argv
[i
], "-arg=", prefixlen
) != 0)
263 args
.push_back(argv
[i
] + prefixlen
);
265 SmallVector
< StringRef
, 20 > clangflags
;
266 SplitString( CLANGFLAGS
, clangflags
);
267 for (auto const & i
: clangflags
) {
268 args
.push_back(i
.str());
272 { // These must match LO_CLANG_ANALYZER_PCH_CXXFLAGS in Makefile-clang.mk .
273 "-I" BUILDDIR
"/config_host" // plugin sources use e.g. config_global.h
274 #if LO_CLANG_USE_ANALYZER_PCH
276 "-include-pch", // use PCH with Clang headers to speed up parsing/analysing
277 BUILDDIR
"/compilerplugins/clang/sharedvisitor/clang.pch"
280 for( ; i
< argc
; ++ i
)
282 std::string contents
= readSourceFile(argv
[i
]);
283 if( contents
.empty())
285 foundSomething
= false;
286 #if CLANG_VERSION >= 100000
287 if( !tooling::runToolOnCodeWithArgs( std::unique_ptr
<FindNamedClassAction
>(new FindNamedClassAction
), contents
, args
, argv
[ i
] ))
289 if( !tooling::runToolOnCodeWithArgs( new FindNamedClassAction
, contents
, args
, argv
[ i
] ))
292 std::cerr
<< "Failed to analyze: " << argv
[ i
] << std::endl
;
295 if( !foundSomething
)
297 // there's #ifndef LO_CLANG_SHARED_PLUGINS in the source, but no class matched
298 std::cerr
<< "Failed to find code: " << argv
[ i
] << std::endl
;