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")
50 // TODO need to learn to handle attributes like "[[maybe_unused]]"
51 if (fn
== SRCDIR
"/binaryurp/source/bridge.cxx")
54 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/vcl/qt5/")
55 || loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/gtk3_kde5/kde5_filepicker_ipc.cxx"))
60 virtual void run() override
63 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
66 bool VisitCompoundStmt(CompoundStmt
const*);
67 bool PreTraverseSwitchStmt(SwitchStmt
*);
68 bool PostTraverseSwitchStmt(SwitchStmt
*, bool);
69 bool TraverseSwitchStmt(SwitchStmt
*);
70 bool VisitSwitchStmt(SwitchStmt
const*);
73 std::vector
<const Stmt
*> switchStmtBodies
;
76 bool Indentation::PreTraverseSwitchStmt(SwitchStmt
* switchStmt
)
78 switchStmtBodies
.push_back(switchStmt
->getBody());
82 bool Indentation::PostTraverseSwitchStmt(SwitchStmt
*, bool)
84 switchStmtBodies
.pop_back();
88 bool Indentation::TraverseSwitchStmt(SwitchStmt
* switchStmt
)
90 PreTraverseSwitchStmt(switchStmt
);
91 auto ret
= FilteringPlugin::TraverseSwitchStmt(switchStmt
);
92 PostTraverseSwitchStmt(switchStmt
, ret
);
96 bool Indentation::VisitCompoundStmt(CompoundStmt
const* compoundStmt
)
98 if (ignoreLocation(compoundStmt
))
100 // these are kind of free form
101 if (!switchStmtBodies
.empty() && switchStmtBodies
.back() == compoundStmt
)
104 constexpr unsigned MAX
= std::numeric_limits
<unsigned>::max();
105 unsigned column
= MAX
;
106 Stmt
const* firstStmt
= nullptr;
107 unsigned curLine
= MAX
;
108 unsigned prevLine
= MAX
;
109 SourceLocation prevEnd
;
110 auto& SM
= compiler
.getSourceManager();
111 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
114 auto const actualPrevEnd
= prevEnd
;
115 prevEnd
= compat::getEndLoc(stmt
); // compute early, before below `continue`s
117 // these show up in macro expansions, not interesting
118 if (isa
<NullStmt
>(stmt
))
120 // these are always weirdly indented
121 if (isa
<LabelStmt
>(stmt
))
123 #if CLANG_VERSION < 100000
125 // <https://github.com/llvm/llvm-project/commit/dc3957ec215dd17b8d293461f18696566637a6cd>
126 // "Include leading attributes in DeclStmt's SourceRange", getBeginLoc of a VarDecl DeclStmt
127 // with an UnusedAttr pointed after the attr (and getLocation of the attr pointed at
128 // "maybe_unused", not at the leading "[["), so just ignore those in old compiler versions:
129 if (auto const declStmt
= dyn_cast
<DeclStmt
>(stmt
))
131 if (declStmt
->decl_begin() != declStmt
->decl_end())
133 if (auto const decl
= dyn_cast
<VarDecl
>(*declStmt
->decl_begin()))
135 if (decl
->hasAttr
<UnusedAttr
>())
144 auto stmtLoc
= compat::getBeginLoc(stmt
);
147 bool partOfMacro
= false;
148 if (SM
.isMacroArgExpansion(stmtLoc
) || SM
.isMacroBodyExpansion(stmtLoc
))
151 macroName
= Lexer::getImmediateMacroNameForDiagnostics(
152 stmtLoc
, compiler
.getSourceManager(), compiler
.getLangOpts());
153 // CPPUNIT_TEST_SUITE/CPPUNIT_TEST/CPPUNIT_TEST_SUITE_END work together, so the one is indented inside the other
154 if (macroName
== "CPPUNIT_TEST_SUITE")
156 // similar thing in dbaccess/
157 if (macroName
== "DECL_PROP_IMPL")
159 // similar thing in forms/
160 if (macroName
== "DECL_IFACE_PROP_IMPL" || macroName
== "DECL_BOOL_PROP_IMPL")
162 #if CLANG_VERSION >= 70000
163 stmtLoc
= SM
.getExpansionRange(stmtLoc
).getBegin();
165 stmtLoc
= SM
.getExpansionRange(stmtLoc
).first
;
169 // check for comment to the left of the statement
171 const char* p1
= SM
.getCharacterData(stmtLoc
);
173 bool foundComment
= false;
174 while (*p1
&& *p1
!= '\n')
187 bool invalid1
= false;
188 bool invalid2
= false;
189 unsigned tmpColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
190 unsigned tmpLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
191 if (invalid1
|| invalid2
)
200 else if (curLine
== prevLine
)
202 // ignore multiple statements on same line
204 else if (column
!= tmpColumn
)
206 if (containsPreprocessingConditionalInclusion(SourceRange(
207 locationAfterToken(compiler
.getSourceManager().getExpansionLoc(actualPrevEnd
)),
208 compiler
.getSourceManager().getExpansionLoc(compat::getBeginLoc(stmt
)))))
210 report(DiagnosticsEngine::Warning
, "statement mis-aligned compared to neighbours %0",
213 report(DiagnosticsEngine::Note
, "measured against this one",
214 compat::getBeginLoc(firstStmt
));
215 //getParentStmt(compoundStmt)->dump();
220 if (auto ifStmt
= dyn_cast
<IfStmt
>(stmt
))
222 auto bodyStmt
= ifStmt
->getThen();
223 if (bodyStmt
&& !isa
<CompoundStmt
>(bodyStmt
))
225 stmtLoc
= compat::getBeginLoc(bodyStmt
);
228 unsigned bodyColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
229 unsigned bodyLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
230 if (invalid1
|| invalid2
)
233 if (bodyLine
!= tmpLine
&& bodyColumn
<= tmpColumn
)
234 report(DiagnosticsEngine::Warning
, "if body should be indented", stmtLoc
);
237 auto elseStmt
= ifStmt
->getElse();
238 if (elseStmt
&& !isa
<CompoundStmt
>(elseStmt
) && !isa
<IfStmt
>(elseStmt
))
240 stmtLoc
= compat::getBeginLoc(elseStmt
);
243 unsigned elseColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
244 unsigned elseLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
245 if (invalid1
|| invalid2
)
247 if (elseLine
!= tmpLine
&& elseColumn
<= tmpColumn
)
248 report(DiagnosticsEngine::Warning
, "else body should be indented", stmtLoc
);
250 if (elseStmt
&& !isa
<CompoundStmt
>(bodyStmt
))
252 stmtLoc
= ifStmt
->getElseLoc();
255 unsigned elseColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
256 unsigned elseLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
257 if (invalid1
|| invalid2
)
259 if (elseLine
!= tmpLine
&& elseColumn
!= tmpColumn
)
260 report(DiagnosticsEngine::Warning
, "if and else not aligned", stmtLoc
);
267 bool Indentation::VisitSwitchStmt(SwitchStmt
const* switchStmt
)
269 if (ignoreLocation(switchStmt
))
272 constexpr unsigned MAX
= std::numeric_limits
<unsigned>::max();
273 unsigned column
= MAX
;
274 Stmt
const* firstStmt
= nullptr;
275 unsigned curLine
= MAX
;
276 unsigned prevLine
= MAX
;
277 auto& SM
= compiler
.getSourceManager();
278 auto compoundStmt
= dyn_cast
<CompoundStmt
>(switchStmt
->getBody());
281 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
283 Stmt
const* caseStmt
= dyn_cast
<CaseStmt
>(*i
);
285 caseStmt
= dyn_cast
<DefaultStmt
>(*i
);
289 auto stmtLoc
= compat::getBeginLoc(caseStmt
);
291 bool invalid1
= false;
292 bool invalid2
= false;
293 unsigned tmpColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
294 unsigned tmpLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
295 if (invalid1
|| invalid2
)
302 firstStmt
= caseStmt
;
304 else if (curLine
== prevLine
)
306 // ignore multiple statements on same line
308 else if (column
!= tmpColumn
)
310 // disable this for now, ends up touching some very large switch statements in sw/ and sc/
312 // report(DiagnosticsEngine::Warning, "statement mis-aligned compared to neighbours",
314 // report(DiagnosticsEngine::Note, "measured against this one",
315 // compat::getBeginLoc(firstStmt));
316 //getParentStmt(compoundStmt)->dump();
323 loplugin::Plugin::Registration
<Indentation
> indentation("indentation");
327 #endif // LO_CLANG_SHARED_PLUGINS
329 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */