Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / compilerplugins / clang / finalclasses.cxx
blob327ff92653c0ee45ef112b8a10ff36ef28dc9413
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 <cassert>
11 #include <set>
12 #include <string>
13 #include <iostream>
14 #include "config_clang.h"
15 #include "plugin.hxx"
16 #include <fstream>
18 /**
19 Look for classes that are final i.e. nothing extends them, and have either
20 (a) protected fields or members.
22 (b) virtual members
24 In the case of (a), those members/fields can be made private.
25 In the case of (b), making the class final means the compiler can devirtualise
26 some method calls
28 The process goes something like this:
29 $ make check
30 $ make FORCE_COMPILE=all COMPILER_PLUGIN_TOOL='finalclasses' check
31 $ ./compilerplugins/clang/finalclasses.py
35 namespace {
37 // try to limit the voluminous output a little
38 static std::set<std::string> inheritedFromSet;
39 static std::map<std::string,std::string> definitionMap; // className -> filename
41 class FinalClasses:
42 public RecursiveASTVisitor<FinalClasses>, public loplugin::Plugin
44 public:
45 explicit FinalClasses(loplugin::InstantiationData const & data):
46 Plugin(data) {}
48 virtual void run() override
50 handler.enableTreeWideAnalysisMode();
52 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
54 // dump all our output in one write call - this is to try and limit IO "crosstalk" between multiple processes
55 // writing to the same logfile
56 std::string output;
57 for (const std::string & s : inheritedFromSet)
58 output += "inherited-from:\t" + s + "\n";
59 for (const auto & s : definitionMap)
60 output += "definition:\t" + s.first + "\t" + s.second + "\n";
61 std::ofstream myfile;
62 myfile.open( WORKDIR "/loplugin.finalclasses.log", std::ios::app | std::ios::out);
63 myfile << output;
64 myfile.close();
67 bool shouldVisitTemplateInstantiations () const { return true; }
69 bool shouldVisitImplicitCode() const { return true; }
71 bool VisitCXXRecordDecl( const CXXRecordDecl* decl);
72 private:
73 void checkBase(QualType qt);
76 bool ignoreClass(StringRef s)
78 // ignore stuff in the standard library, and UNO stuff we can't touch.
79 if (s.startswith("rtl::") || s.startswith("sal::") || s.startswith("com::sun::")
80 || s.startswith("std::") || s.startswith("boost::")
81 || s == "OString" || s == "OUString" || s == "bad_alloc")
83 return true;
85 return false;
88 bool FinalClasses::VisitCXXRecordDecl(const CXXRecordDecl* decl)
90 if (ignoreLocation(decl))
91 return true;
92 if (!decl->hasDefinition())
93 return true;
95 for (auto it = decl->bases_begin(); it != decl->bases_end(); ++it)
97 const CXXBaseSpecifier spec = *it;
98 checkBase(spec.getType());
100 for (auto it = decl->vbases_begin(); it != decl->vbases_end(); ++it)
102 const CXXBaseSpecifier spec = *it;
103 checkBase(spec.getType());
106 if (decl->hasAttr<FinalAttr>())
107 return true;
108 bool bFoundVirtual = false;
109 bool bFoundProtected = false;
110 for (auto it = decl->method_begin(); it != decl->method_end(); ++it) {
111 auto i = *it;
112 // ignore methods that are overriding base-class methods, making them private
113 // isn't useful
114 if ( !i->hasAttr<OverrideAttr>() && i->getAccess() == AS_protected )
115 bFoundProtected = true;
116 if ( i->isVirtual() )
117 bFoundVirtual = true;
120 if (!bFoundProtected)
122 for (auto it = decl->field_begin(); it != decl->field_end(); ++it) {
123 auto i = *it;
124 if ( i->getAccess() == AS_protected ) {
125 bFoundProtected = true;
126 break;
130 if (!bFoundProtected && !bFoundVirtual)
131 return true;
133 std::string s = decl->getQualifiedNameAsString();
134 if (ignoreClass(s))
135 return true;
137 SourceLocation spellingLocation = compiler.getSourceManager().getSpellingLoc(decl->getBeginLoc());
138 auto const filename = getFilenameOfLocation(spellingLocation);
139 auto sourceLocation = filename.substr(strlen(SRCDIR)).str() + ":"
140 + std::to_string(compiler.getSourceManager().getSpellingLineNumber(spellingLocation));
141 definitionMap.insert( std::pair<std::string,std::string>(s, sourceLocation) );
142 return true;
145 void FinalClasses::checkBase(QualType baseType)
147 // need to look through typedefs, hence the getUnqualifiedDesugaredType
148 baseType = baseType.getDesugaredType(compiler.getASTContext());
149 std::string x;
150 // so that we get just the template name, excluding the template parameters
151 if (baseType->isRecordType())
152 x = baseType->getAsCXXRecordDecl()->getQualifiedNameAsString();
153 else if (auto templateType = baseType->getAs<TemplateSpecializationType>())
154 x = templateType->getTemplateName().getAsTemplateDecl()->getQualifiedNameAsString();
155 else
156 x = baseType.getAsString();
157 inheritedFromSet.insert( x );
160 loplugin::Plugin::Registration< FinalClasses > X("finalclasses", false);
164 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */