LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / compilerplugins / clang / sharedvisitor / analyzer.cxx
blob381bd03759edf1e81ef765e48392b818cdd982a4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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"
17 #include <cassert>
18 #include <cstddef>
19 #include <cstring>
20 #include <iostream>
21 #include <memory>
22 #include <fstream>
23 #include <set>
25 #include "config_clang.h"
26 #include "../check.hxx"
27 #include "../check.cxx"
29 using namespace clang;
30 using namespace llvm;
32 using namespace loplugin;
34 // Info about a Traverse* function in a plugin.
35 struct TraverseFunctionInfo
37 std::string name;
38 std::string argument;
39 bool hasPre = false;
40 bool hasPost = false;
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 >
56 public:
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())
67 return true;
69 return RecursiveASTVisitor<CheckFileVisitor>::TraverseNamespaceDecl(decl);
72 private:
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());
84 return type;
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 );
104 return info;
107 static bool foundSomething;
109 bool CheckFileVisitor::VisitCXXRecordDecl( CXXRecordDecl* decl )
111 if( !isDerivedFrom( decl, inheritsPluginClassCheck ))
112 return true;
114 if( decl->getName() == "FilteringPlugin" || decl->getName() == "FilteringRewritePlugin" )
115 return true;
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())
125 continue;
126 if( method->isStatic() || method->getAccess() != AS_public )
127 continue;
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()
137 << std::endl;
138 std::cout << "VisitFunctionEnd" << std::endl;
140 else
142 std::cerr << "Unhandled Visit* function: " << decl->getName().str()
143 << "::" << method->getName().str() << std::endl;
144 abort();
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 ));
155 else
157 std::cerr << "Unhandled Traverse* function: " << decl->getName().str()
158 << "::" << method->getName().str() << std::endl;
159 abort();
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;
182 abort();
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;
198 return true;
201 class FindNamedClassConsumer
202 : public ASTConsumer
204 public:
205 void Initialize(ASTContext& context) override
207 visitor.setContext(context);
209 virtual void HandleTranslationUnit(ASTContext& context) override
211 visitor.TraverseDecl( context.getTranslationUnitDecl());
213 private:
214 CheckFileVisitor visitor;
217 class FindNamedClassAction
218 : public ASTFrontendAction
220 public:
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 );
232 if( !stream )
234 std::cerr << "Failed to open: " << filename << std::endl;
235 exit( 1 );
237 std::string line;
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 )
243 hasIfdef = true;
244 contents += line;
245 contents += '\n';
247 if( stream.eof() && hasIfdef )
248 return contents;
249 return "";
252 int main(int argc, char** argv)
254 std::vector< std::string > args;
255 int i = 1;
256 for( ; i < argc; ++ i )
258 constexpr std::size_t prefixlen = 5; // strlen("-arg=");
259 if (std::strncmp(argv[i], "-arg=", prefixlen) != 0)
261 break;
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());
270 args.insert(
271 args.end(),
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"
278 #endif
280 for( ; i < argc; ++ i )
282 std::string contents = readSourceFile(argv[i]);
283 if( contents.empty())
284 continue;
285 foundSomething = false;
286 #if CLANG_VERSION >= 100000
287 if( !tooling::runToolOnCodeWithArgs( std::unique_ptr<FindNamedClassAction>(new FindNamedClassAction), contents, args, argv[ i ] ))
288 #else
289 if( !tooling::runToolOnCodeWithArgs( new FindNamedClassAction, contents, args, argv[ i ] ))
290 #endif
292 std::cerr << "Failed to analyze: " << argv[ i ] << std::endl;
293 return 2;
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;
299 return 2;
302 return 0;