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
21 Check for child statements inside a compound statement that do not share the same indentation.
23 TODO if an open-brace starts on a new line by itself, check that it lines up with it's closing-brace
24 TODO else should line up with if
29 class Indentation
: public loplugin::FilteringPlugin
<Indentation
>
32 explicit Indentation(loplugin::InstantiationData
const& data
)
33 : FilteringPlugin(data
)
37 virtual bool preRun() override
39 std::string
fn(handler
.getMainFileName());
40 loplugin::normalizeDotDotInFilePath(fn
);
41 // include another file to build a table
42 if (fn
== SRCDIR
"/sc/source/core/tool/cellkeytranslator.cxx")
45 if (fn
== SRCDIR
"/sc/source/core/tool/compiler.cxx")
47 // looks like lex/yacc output
48 if (fn
== SRCDIR
"/hwpfilter/source/grammar.cxx")
51 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/vcl/qt5/")
52 || loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/gtk3_kde5/kde5_filepicker_ipc.cxx"))
57 virtual void run() override
60 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
63 bool VisitCompoundStmt(CompoundStmt
const*);
64 bool PreTraverseSwitchStmt(SwitchStmt
*);
65 bool PostTraverseSwitchStmt(SwitchStmt
*, bool);
66 bool TraverseSwitchStmt(SwitchStmt
*);
67 bool VisitSwitchStmt(SwitchStmt
const*);
70 std::vector
<const Stmt
*> switchStmtBodies
;
73 bool Indentation::PreTraverseSwitchStmt(SwitchStmt
* switchStmt
)
75 switchStmtBodies
.push_back(switchStmt
->getBody());
79 bool Indentation::PostTraverseSwitchStmt(SwitchStmt
*, bool)
81 switchStmtBodies
.pop_back();
85 bool Indentation::TraverseSwitchStmt(SwitchStmt
* switchStmt
)
87 PreTraverseSwitchStmt(switchStmt
);
88 auto ret
= FilteringPlugin::TraverseSwitchStmt(switchStmt
);
89 PostTraverseSwitchStmt(switchStmt
, ret
);
93 bool Indentation::VisitCompoundStmt(CompoundStmt
const* compoundStmt
)
95 if (ignoreLocation(compoundStmt
))
97 // these are kind of free form
98 if (!switchStmtBodies
.empty() && switchStmtBodies
.back() == compoundStmt
)
101 constexpr unsigned MAX
= std::numeric_limits
<unsigned>::max();
102 unsigned column
= MAX
;
103 Stmt
const* firstStmt
= nullptr;
104 unsigned curLine
= MAX
;
105 unsigned prevLine
= MAX
;
106 SourceLocation prevEnd
;
107 auto& SM
= compiler
.getSourceManager();
108 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
111 auto const actualPrevEnd
= prevEnd
;
112 prevEnd
= compat::getEndLoc(stmt
); // compute early, before below `continue`s
114 // these show up in macro expansions, not interesting
115 if (isa
<NullStmt
>(stmt
))
117 // these are always weirdly indented
118 if (isa
<LabelStmt
>(stmt
))
120 #if CLANG_VERSION < 100000
122 // <https://github.com/llvm/llvm-project/commit/dc3957ec215dd17b8d293461f18696566637a6cd>
123 // "Include leading attributes in DeclStmt's SourceRange", getBeginLoc of a VarDecl DeclStmt
124 // with an UnusedAttr pointed after the attr (and getLocation of the attr pointed at
125 // "maybe_unused", not at the leading "[["), so just ignore those in old compiler versions:
126 if (auto const declStmt
= dyn_cast
<DeclStmt
>(stmt
))
128 if (declStmt
->decl_begin() != declStmt
->decl_end())
130 if (auto const decl
= dyn_cast
<VarDecl
>(*declStmt
->decl_begin()))
132 if (decl
->hasAttr
<UnusedAttr
>())
141 auto stmtLoc
= compat::getBeginLoc(stmt
);
144 bool partOfMacro
= false;
145 if (SM
.isMacroArgExpansion(stmtLoc
) || SM
.isMacroBodyExpansion(stmtLoc
))
148 macroName
= Lexer::getImmediateMacroNameForDiagnostics(
149 stmtLoc
, compiler
.getSourceManager(), compiler
.getLangOpts());
150 // CPPUNIT_TEST_SUITE/CPPUNIT_TEST/CPPUNIT_TEST_SUITE_END work together, so the one is indented inside the other
151 if (macroName
== "CPPUNIT_TEST_SUITE")
153 // similar thing in dbaccess/
154 if (macroName
== "DECL_PROP_IMPL")
156 // similar thing in forms/
157 if (macroName
== "DECL_IFACE_PROP_IMPL" || macroName
== "DECL_BOOL_PROP_IMPL")
159 #if CLANG_VERSION >= 70000
160 stmtLoc
= SM
.getExpansionRange(stmtLoc
).getBegin();
162 stmtLoc
= SM
.getExpansionRange(stmtLoc
).first
;
166 // check for comment to the left of the statement
168 const char* p1
= SM
.getCharacterData(stmtLoc
);
170 bool foundComment
= false;
171 while (*p1
&& *p1
!= '\n')
184 bool invalid1
= false;
185 bool invalid2
= false;
186 unsigned tmpColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
187 unsigned tmpLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
188 if (invalid1
|| invalid2
)
197 else if (curLine
== prevLine
)
199 // ignore multiple statements on same line
201 else if (column
!= tmpColumn
)
203 if (containsPreprocessingConditionalInclusion(SourceRange(
204 locationAfterToken(compiler
.getSourceManager().getExpansionLoc(actualPrevEnd
)),
205 compiler
.getSourceManager().getExpansionLoc(compat::getBeginLoc(stmt
)))))
207 report(DiagnosticsEngine::Warning
, "statement mis-aligned compared to neighbours %0",
210 report(DiagnosticsEngine::Note
, "measured against this one",
211 compat::getBeginLoc(firstStmt
));
212 //getParentStmt(compoundStmt)->dump();
217 if (auto ifStmt
= dyn_cast
<IfStmt
>(stmt
))
219 auto bodyStmt
= ifStmt
->getThen();
220 if (bodyStmt
&& !isa
<CompoundStmt
>(bodyStmt
))
222 stmtLoc
= compat::getBeginLoc(bodyStmt
);
225 unsigned bodyColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
226 unsigned bodyLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
227 if (invalid1
|| invalid2
)
230 if (bodyLine
!= tmpLine
&& bodyColumn
<= tmpColumn
)
231 report(DiagnosticsEngine::Warning
, "if body should be indented", stmtLoc
);
234 auto elseStmt
= ifStmt
->getElse();
235 if (elseStmt
&& !isa
<CompoundStmt
>(elseStmt
) && !isa
<IfStmt
>(elseStmt
))
237 stmtLoc
= compat::getBeginLoc(elseStmt
);
240 unsigned elseColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
241 unsigned elseLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
242 if (invalid1
|| invalid2
)
244 if (elseLine
!= tmpLine
&& elseColumn
<= tmpColumn
)
245 report(DiagnosticsEngine::Warning
, "else body should be indented", stmtLoc
);
247 if (elseStmt
&& !isa
<CompoundStmt
>(bodyStmt
))
249 stmtLoc
= ifStmt
->getElseLoc();
252 unsigned elseColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
253 unsigned elseLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
254 if (invalid1
|| invalid2
)
256 if (elseLine
!= tmpLine
&& elseColumn
!= tmpColumn
)
257 report(DiagnosticsEngine::Warning
, "if and else not aligned", stmtLoc
);
264 bool Indentation::VisitSwitchStmt(SwitchStmt
const* switchStmt
)
266 if (ignoreLocation(switchStmt
))
269 constexpr unsigned MAX
= std::numeric_limits
<unsigned>::max();
270 unsigned column
= MAX
;
271 Stmt
const* firstStmt
= nullptr;
272 unsigned curLine
= MAX
;
273 unsigned prevLine
= MAX
;
274 auto& SM
= compiler
.getSourceManager();
275 auto compoundStmt
= dyn_cast
<CompoundStmt
>(switchStmt
->getBody());
278 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
280 Stmt
const* caseStmt
= dyn_cast
<CaseStmt
>(*i
);
282 caseStmt
= dyn_cast
<DefaultStmt
>(*i
);
286 auto stmtLoc
= compat::getBeginLoc(caseStmt
);
288 bool invalid1
= false;
289 bool invalid2
= false;
290 unsigned tmpColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
291 unsigned tmpLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
292 if (invalid1
|| invalid2
)
299 firstStmt
= caseStmt
;
301 else if (curLine
== prevLine
)
303 // ignore multiple statements on same line
305 else if (column
!= tmpColumn
)
307 // disable this for now, ends up touching some very large switch statements in sw/ and sc/
309 // report(DiagnosticsEngine::Warning, "statement mis-aligned compared to neighbours",
311 // report(DiagnosticsEngine::Note, "measured against this one",
312 // compat::getBeginLoc(firstStmt));
313 //getParentStmt(compoundStmt)->dump();
320 loplugin::Plugin::Registration
<Indentation
> indentation("indentation");
324 #endif // LO_CLANG_SHARED_PLUGINS
326 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */