1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
7 * This file is distributed under the University of Illinois Open Source
8 * License. See LICENSE.TXT for details.
11 #ifndef LO_CLANG_SHARED_PLUGINS
26 class NamespaceIndentation
: public loplugin::FilteringPlugin
<NamespaceIndentation
>
29 explicit NamespaceIndentation(loplugin::InstantiationData
const& data
)
30 : FilteringPlugin(data
)
34 virtual bool preRun() override
{ return true; }
36 virtual void run() override
39 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
42 bool VisitNamespaceDecl(NamespaceDecl
const*);
45 std::string
GetFullNamespace(const NamespaceDecl
* nsDecl
);
48 void trim(std::string
& str
)
51 auto it1
= std::find_if(str
.rbegin(), str
.rend(), [](char ch
) {
52 return !std::isspace
<char>(ch
, std::locale::classic());
54 str
.erase(it1
.base(), str
.end());
56 auto it2
= std::find_if(str
.begin(), str
.end(), [](char ch
) {
57 return !std::isspace
<char>(ch
, std::locale::classic());
59 str
.erase(str
.begin(), it2
);
62 bool NamespaceIndentation::VisitNamespaceDecl(NamespaceDecl
const* nsDecl
)
64 if (ignoreLocation(nsDecl
))
66 if (nsDecl
->isAnonymousNamespace())
68 if (isInUnoIncludeFile(compiler
.getSourceManager().getSpellingLoc(nsDecl
->getLocation())))
71 // right now, just fixing up the fallout from clang-tidy-modernize-namespaces, which
72 // does not touch header files
73 if (!compiler
.getSourceManager().isInMainFile(nsDecl
->getLocation()))
76 auto& SM
= compiler
.getSourceManager();
78 // if we have a combined ns (.e.g namespace aaa::bbb), this appears in the AST
79 // as two nested namespace sharing the same source locations, so ignore the outer decls
80 if (!nsDecl
->decls_empty())
82 auto child
= dyn_cast_or_null
<NamespaceDecl
>(*nsDecl
->decls_begin());
85 bool invalid1
= false;
86 bool invalid2
= false;
87 unsigned line1
= SM
.getPresumedLineNumber(compat::getBeginLoc(nsDecl
), &invalid1
);
88 unsigned line2
= SM
.getPresumedLineNumber(compat::getBeginLoc(child
), &invalid2
);
94 // Truly hacky way to find the actual beginning of an xxx::yyy namespace declaration
95 // if we are inside the yyy NameSpaceDecl of
97 // the beginLoc is just between the "xxx" and the "::"
98 auto nsDeclBeginLoc
= compat::getBeginLoc(nsDecl
);
99 bool foundMultiple
= false;
101 constexpr int BACKSCAN
= 32;
102 auto beginLoc
= compat::getBeginLoc(nsDecl
).getLocWithOffset(-BACKSCAN
);
103 auto endLoc
= compat::getBeginLoc(nsDecl
).getLocWithOffset(3);
104 const char* p1
= SM
.getCharacterData(beginLoc
);
105 const char* p2
= SM
.getCharacterData(endLoc
);
106 unsigned n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
107 if (p2
< p1
|| n
> 128 || (p2
- p1
+ n
) > 2048)
109 auto s
= std::string(p1
, p2
- p1
);
110 auto idx1
= s
.rfind(" "); // find the space preceding the namespace token
111 if (idx1
!= std::string::npos
)
113 auto namespaceToken
= s
.substr(idx1
);
114 if (namespaceToken
.find("::") != std::string::npos
)
116 auto idx
= s
.rfind("\n");
117 nsDeclBeginLoc
= compat::getBeginLoc(nsDecl
).getLocWithOffset(idx
- BACKSCAN
+ 1);
118 foundMultiple
= true;
123 // for now, I am only interested in fixing the fallout from clang-tidy-modernize-namespace, not
128 bool invalid1
= false;
129 bool invalid2
= false;
130 unsigned col1
= SM
.getPresumedColumnNumber(nsDeclBeginLoc
, &invalid1
);
131 unsigned col2
= SM
.getPresumedColumnNumber(nsDecl
->getRBraceLoc(), &invalid2
);
132 unsigned line1
= SM
.getPresumedLineNumber(nsDeclBeginLoc
, &invalid1
);
133 unsigned line2
= SM
.getPresumedLineNumber(nsDecl
->getRBraceLoc(), &invalid2
);
134 if (invalid1
|| invalid2
)
136 if (line1
== line2
) // single line declaration
139 report(DiagnosticsEngine::Warning
, "statement right brace mis-aligned",
140 nsDecl
->getRBraceLoc());
142 // no easy way to get the position of the left brace
143 auto endLoc
= compat::getBeginLoc(nsDecl
).getLocWithOffset(256);
144 const char* p1
= SM
.getCharacterData(SM
.getExpansionLoc(compat::getBeginLoc(nsDecl
)));
145 const char* p2
= SM
.getCharacterData(SM
.getExpansionLoc(endLoc
));
146 unsigned n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
147 if (p2
< p1
|| n
> 128 || (p2
- p1
+ n
) > 2048)
149 auto s
= std::string(p1
, p2
- p1
+ n
);
150 auto idx1
= s
.find("\n");
151 auto idx2
= s
.find("{");
152 if (idx1
!= std::string::npos
&& idx2
!= std::string::npos
)
155 auto col3
= idx2
- idx1
;
157 report(DiagnosticsEngine::Warning
, "statement left brace mis-aligned",
158 compat::getBeginLoc(nsDecl
));
161 // extract the comment following the end brace
162 auto beginLoc
= nsDecl
->getRBraceLoc();
163 endLoc
= beginLoc
.getLocWithOffset(128);
164 p1
= SM
.getCharacterData(SM
.getExpansionLoc(beginLoc
));
165 p2
= SM
.getCharacterData(SM
.getExpansionLoc(endLoc
));
166 n
= Lexer::MeasureTokenLength(endLoc
, SM
, compiler
.getLangOpts());
167 if (p2
< p1
|| n
> 128 || (p2
- p1
+ n
) > 2048)
169 s
= std::string(p1
, p2
- p1
+ n
);
172 if (idx1
!= std::string::npos
&& idx2
!= std::string::npos
&& idx1
< idx2
)
175 s
= s
.substr(idx1
, idx2
- idx1
);
177 std::string fullNamespace
= GetFullNamespace(nsDecl
);
178 if (!(s
== fullNamespace
|| s
== (fullNamespace
+ " namespace") || s
== "namespace"
179 || s
== ("namespace " + fullNamespace
) || s
== ("namespace ::" + fullNamespace
)
180 || s
== ("end " + fullNamespace
) || s
== "end namespace"
181 || s
== ("end namespace " + fullNamespace
)
182 || s
== ("end " + fullNamespace
+ " namespace") || s
== "end of namespace"
183 || s
== ("end of namespace " + fullNamespace
)
184 || s
== ("end of namespace ::" + fullNamespace
)
185 || s
== ("eof of namespace " + fullNamespace
)))
187 report(DiagnosticsEngine::Warning
, "incorrect comment at end of namespace %0",
188 nsDecl
->getRBraceLoc())
196 std::string
NamespaceIndentation::GetFullNamespace(const NamespaceDecl
* nsDecl
)
198 std::vector
<llvm::StringRef
> names
;
202 names
.push_back(ns
->getName());
203 ns
= dyn_cast
<NamespaceDecl
>(ns
->getParent());
205 std::string fullNamespace
;
206 for (auto it
= names
.rbegin(); it
!= names
.rend(); ++it
)
207 fullNamespace
+= "::" + it
->str();
208 fullNamespace
= fullNamespace
.substr(2);
209 return fullNamespace
;
212 // leave this off by default, so as not to annoy people
213 loplugin::Plugin::Registration
<NamespaceIndentation
> namespaceindentation("namespaceindentation",
218 #endif // LO_CLANG_SHARED_PLUGINS
220 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */