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