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.
20 Check for child statements inside a compound statement that do not share the same indentation.
22 TODO if an open-brace starts on a new line by itself, check that it lines up with it's closing-brace
23 TODO else should line up with if
28 class Indentation
: public loplugin::FilteringPlugin
<Indentation
>
31 explicit Indentation(loplugin::InstantiationData
const& data
)
32 : FilteringPlugin(data
)
36 virtual void run() override
38 std::string
fn(handler
.getMainFileName());
39 loplugin::normalizeDotDotInFilePath(fn
);
40 // include another file to build a table
41 if (fn
== SRCDIR
"/sc/source/core/tool/cellkeytranslator.cxx")
44 if (fn
== SRCDIR
"/sc/source/core/tool/compiler.cxx")
46 // looks like lex/yacc output
47 if (fn
== SRCDIR
"/hwpfilter/source/grammar.cxx")
49 // TODO need to learn to handle attributes like "[[maybe_unused]]"
50 if (fn
== SRCDIR
"/binaryurp/source/bridge.cxx")
53 if (loplugin::hasPathnamePrefix(fn
, SRCDIR
"/vcl/qt5/")
54 || loplugin::isSamePathname(fn
, SRCDIR
"/vcl/unx/gtk3_kde5/kde5_filepicker_ipc.cxx"))
56 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
59 bool VisitCompoundStmt(CompoundStmt
const*);
60 bool TraverseSwitchStmt(SwitchStmt
*);
61 bool VisitSwitchStmt(SwitchStmt
const*);
64 Stmt
const* switchStmtBody
= nullptr;
67 bool Indentation::TraverseSwitchStmt(SwitchStmt
* switchStmt
)
69 auto prev
= switchStmtBody
;
70 switchStmtBody
= switchStmt
->getBody();
71 FilteringPlugin::TraverseSwitchStmt(switchStmt
);
72 switchStmtBody
= prev
;
76 bool Indentation::VisitCompoundStmt(CompoundStmt
const* compoundStmt
)
78 if (ignoreLocation(compoundStmt
))
80 // these are kind of free form
81 if (switchStmtBody
== compoundStmt
)
84 constexpr unsigned MAX
= std::numeric_limits
<unsigned>::max();
85 unsigned column
= MAX
;
86 Stmt
const* firstStmt
= nullptr;
87 unsigned curLine
= MAX
;
88 unsigned prevLine
= MAX
;
89 SourceLocation prevEnd
;
90 auto& SM
= compiler
.getSourceManager();
91 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
94 auto const actualPrevEnd
= prevEnd
;
95 prevEnd
= compat::getEndLoc(stmt
); // compute early, before below `continue`s
97 // these show up in macro expansions, not interesting
98 if (isa
<NullStmt
>(stmt
))
100 // these are always weirdly indented
101 if (isa
<LabelStmt
>(stmt
))
104 auto stmtLoc
= compat::getBeginLoc(stmt
);
107 if (SM
.isMacroArgExpansion(stmtLoc
) || SM
.isMacroBodyExpansion(stmtLoc
))
109 macroName
= Lexer::getImmediateMacroNameForDiagnostics(
110 stmtLoc
, compiler
.getSourceManager(), compiler
.getLangOpts());
111 // CPPUNIT_TEST_SUITE/CPPUNIT_TEST/CPPUNIT_TEST_SUITE_END work together, so the one is indented inside the other
112 if (macroName
== "CPPUNIT_TEST_SUITE")
114 // similar thing in dbaccess/
115 if (macroName
== "DECL_PROP_IMPL")
117 // similar thing in forms/
118 if (macroName
== "DECL_IFACE_PROP_IMPL" || macroName
== "DECL_BOOL_PROP_IMPL")
120 #if CLANG_VERSION >= 70000
121 stmtLoc
= SM
.getExpansionRange(stmtLoc
).getBegin();
123 stmtLoc
= SM
.getExpansionRange(stmtLoc
).first
;
127 // check for comment to the left of the statement
129 const char* p1
= SM
.getCharacterData(stmtLoc
);
131 bool foundComment
= false;
132 while (*p1
&& *p1
!= '\n')
145 bool invalid1
= false;
146 bool invalid2
= false;
147 unsigned tmpColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
148 unsigned tmpLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
149 if (invalid1
|| invalid2
)
158 else if (curLine
== prevLine
)
160 // ignore multiple statements on same line
162 else if (column
!= tmpColumn
)
164 if (containsPreprocessingConditionalInclusion(SourceRange(
165 locationAfterToken(compiler
.getSourceManager().getExpansionLoc(actualPrevEnd
)),
166 compiler
.getSourceManager().getExpansionLoc(compat::getBeginLoc(stmt
)))))
168 report(DiagnosticsEngine::Warning
, "statement mis-aligned compared to neighbours %0",
171 report(DiagnosticsEngine::Note
, "measured against this one",
172 compat::getBeginLoc(firstStmt
));
173 //getParentStmt(compoundStmt)->dump();
180 bool Indentation::VisitSwitchStmt(SwitchStmt
const* switchStmt
)
182 if (ignoreLocation(switchStmt
))
185 constexpr unsigned MAX
= std::numeric_limits
<unsigned>::max();
186 unsigned column
= MAX
;
187 Stmt
const* firstStmt
= nullptr;
188 unsigned curLine
= MAX
;
189 unsigned prevLine
= MAX
;
190 auto& SM
= compiler
.getSourceManager();
191 auto compoundStmt
= dyn_cast
<CompoundStmt
>(switchStmt
->getBody());
194 for (auto i
= compoundStmt
->body_begin(); i
!= compoundStmt
->body_end(); ++i
)
196 Stmt
const* caseStmt
= dyn_cast
<CaseStmt
>(*i
);
198 caseStmt
= dyn_cast
<DefaultStmt
>(*i
);
202 auto stmtLoc
= compat::getBeginLoc(caseStmt
);
204 bool invalid1
= false;
205 bool invalid2
= false;
206 unsigned tmpColumn
= SM
.getPresumedColumnNumber(stmtLoc
, &invalid1
);
207 unsigned tmpLine
= SM
.getPresumedLineNumber(stmtLoc
, &invalid2
);
208 if (invalid1
|| invalid2
)
215 firstStmt
= caseStmt
;
217 else if (curLine
== prevLine
)
219 // ignore multiple statements on same line
221 else if (column
!= tmpColumn
)
223 // disable this for now, ends up touching some very large switch statements in sw/ and sc/
225 // report(DiagnosticsEngine::Warning, "statement mis-aligned compared to neighbours",
227 // report(DiagnosticsEngine::Note, "measured against this one",
228 // compat::getBeginLoc(firstStmt));
229 //getParentStmt(compoundStmt)->dump();
236 loplugin::Plugin::Registration
<Indentation
> X("indentation");
240 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */