#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/FixIt.h"
#include "llvm/ADT/SmallVector.h"
namespace tidy {
namespace readability {
-static const char ReturnStr[] = "return";
-static const char ContinueStr[] = "continue";
-static const char BreakStr[] = "break";
-static const char ThrowStr[] = "throw";
+namespace {
+
+class PPConditionalCollector : public PPCallbacks {
+public:
+ PPConditionalCollector(
+ ElseAfterReturnCheck::ConditionalBranchMap &Collections,
+ const SourceManager &SM)
+ : Collections(Collections), SM(SM) {}
+ void Endif(SourceLocation Loc, SourceLocation IfLoc) override {
+ if (!SM.isWrittenInSameFile(Loc, IfLoc))
+ return;
+ SmallVectorImpl<SourceRange> &Collection = Collections[SM.getFileID(Loc)];
+ assert(Collection.empty() || Collection.back().getEnd() < Loc);
+ Collection.emplace_back(IfLoc, Loc);
+ }
+
+private:
+ ElseAfterReturnCheck::ConditionalBranchMap &Collections;
+ const SourceManager &SM;
+};
+
+} // namespace
+
+static const char InterruptingStr[] = "interrupting";
static const char WarningMessage[] = "do not use 'else' after '%0'";
static const char WarnOnUnfixableStr[] = "WarnOnUnfixable";
static const char WarnOnConditionVariablesStr[] = "WarnOnConditionVariables";
Options.store(Opts, WarnOnConditionVariablesStr, WarnOnConditionVariables);
}
+void ElseAfterReturnCheck::registerPPCallbacks(const SourceManager &SM,
+ Preprocessor *PP,
+ Preprocessor *ModuleExpanderPP) {
+ PP->addPPCallbacks(
+ std::make_unique<PPConditionalCollector>(this->PPConditionals, SM));
+}
+
void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) {
- const auto InterruptsControlFlow =
- stmt(anyOf(returnStmt().bind(ReturnStr), continueStmt().bind(ContinueStr),
- breakStmt().bind(BreakStr),
- expr(ignoringImplicit(cxxThrowExpr().bind(ThrowStr)))));
+ const auto InterruptsControlFlow = stmt(anyOf(
+ returnStmt().bind(InterruptingStr), continueStmt().bind(InterruptingStr),
+ breakStmt().bind(InterruptingStr),
+ expr(ignoringImplicit(cxxThrowExpr().bind(InterruptingStr)))));
Finder->addMatcher(
compoundStmt(
forEach(ifStmt(unless(isConstexpr()),
this);
}
+static bool hasPreprocessorBranchEndBetweenLocations(
+ const ElseAfterReturnCheck::ConditionalBranchMap &ConditionalBranchMap,
+ const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLoc) {
+
+ SourceLocation ExpandedStartLoc = SM.getExpansionLoc(StartLoc);
+ SourceLocation ExpandedEndLoc = SM.getExpansionLoc(EndLoc);
+ if (!SM.isWrittenInSameFile(ExpandedStartLoc, ExpandedEndLoc))
+ return false;
+
+ // StartLoc and EndLoc expand to the same macro.
+ if (ExpandedStartLoc == ExpandedEndLoc)
+ return false;
+
+ assert(ExpandedStartLoc < ExpandedEndLoc);
+
+ auto Iter = ConditionalBranchMap.find(SM.getFileID(ExpandedEndLoc));
+
+ if (Iter == ConditionalBranchMap.end() || Iter->getSecond().empty())
+ return false;
+
+ const SmallVectorImpl<SourceRange> &ConditionalBranches = Iter->getSecond();
+
+ assert(llvm::is_sorted(ConditionalBranches,
+ [](const SourceRange &LHS, const SourceRange &RHS) {
+ return LHS.getEnd() < RHS.getEnd();
+ }));
+
+ // First conditional block that ends after ExpandedStartLoc.
+ const auto *Begin =
+ llvm::lower_bound(ConditionalBranches, ExpandedStartLoc,
+ [](const SourceRange &LHS, const SourceLocation &RHS) {
+ return LHS.getEnd() < RHS;
+ });
+ const auto *End = ConditionalBranches.end();
+ for (; Begin != End && Begin->getEnd() < ExpandedEndLoc; ++Begin)
+ if (Begin->getBegin() < ExpandedStartLoc)
+ return true;
+ return false;
+}
+
+static StringRef getControlFlowString(const Stmt &Stmt) {
+ if (isa<ReturnStmt>(Stmt))
+ return "return";
+ if (isa<ContinueStmt>(Stmt))
+ return "continue";
+ if (isa<BreakStmt>(Stmt))
+ return "break";
+ if (isa<CXXThrowExpr>(Stmt))
+ return "throw";
+ llvm_unreachable("Unknown control flow interruptor");
+}
+
void ElseAfterReturnCheck::check(const MatchFinder::MatchResult &Result) {
const auto *If = Result.Nodes.getNodeAs<IfStmt>("if");
const auto *Else = Result.Nodes.getNodeAs<Stmt>("else");
const auto *OuterScope = Result.Nodes.getNodeAs<CompoundStmt>("cs");
-
- bool IsLastInScope = OuterScope->body_back() == If;
+ const auto *Interrupt = Result.Nodes.getNodeAs<Stmt>(InterruptingStr);
SourceLocation ElseLoc = If->getElseLoc();
- auto ControlFlowInterruptor = [&]() -> llvm::StringRef {
- for (llvm::StringRef BindingName :
- {ReturnStr, ContinueStr, BreakStr, ThrowStr})
- if (Result.Nodes.getNodeAs<Stmt>(BindingName))
- return BindingName;
- return {};
- }();
+ if (hasPreprocessorBranchEndBetweenLocations(
+ PPConditionals, *Result.SourceManager, Interrupt->getBeginLoc(),
+ ElseLoc))
+ return;
+
+ bool IsLastInScope = OuterScope->body_back() == If;
+ StringRef ControlFlowInterruptor = getControlFlowString(*Interrupt);
if (!IsLastInScope && containsDeclInScope(Else)) {
if (WarnOnUnfixable) {