1 //===--- SuspiciousSemicolonCheck.cpp - clang-tidy-------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "SuspiciousSemicolonCheck.h"
10 #include "../utils/LexerUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::bugprone
{
18 void SuspiciousSemicolonCheck::registerMatchers(MatchFinder
*Finder
) {
20 stmt(anyOf(ifStmt(hasThen(nullStmt().bind("semi")),
21 unless(hasElse(stmt())),
22 unless(isConstexpr())),
23 forStmt(hasBody(nullStmt().bind("semi"))),
24 cxxForRangeStmt(hasBody(nullStmt().bind("semi"))),
25 whileStmt(hasBody(nullStmt().bind("semi")))))
30 void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult
&Result
) {
31 if (Result
.Context
->getDiagnostics().hasUncompilableErrorOccurred())
34 const auto *Semicolon
= Result
.Nodes
.getNodeAs
<NullStmt
>("semi");
35 SourceLocation LocStart
= Semicolon
->getBeginLoc();
37 if (LocStart
.isMacroID())
40 ASTContext
&Ctxt
= *Result
.Context
;
41 auto Token
= utils::lexer::getPreviousToken(LocStart
, Ctxt
.getSourceManager(),
43 auto &SM
= *Result
.SourceManager
;
44 unsigned SemicolonLine
= SM
.getSpellingLineNumber(LocStart
);
46 const auto *Statement
= Result
.Nodes
.getNodeAs
<Stmt
>("stmt");
47 const bool IsIfStmt
= isa
<IfStmt
>(Statement
);
50 SM
.getSpellingLineNumber(Token
.getLocation()) != SemicolonLine
)
53 SourceLocation LocEnd
= Semicolon
->getEndLoc();
54 FileID FID
= SM
.getFileID(LocEnd
);
55 llvm::MemoryBufferRef Buffer
= SM
.getBufferOrFake(FID
, LocEnd
);
56 Lexer
Lexer(SM
.getLocForStartOfFile(FID
), Ctxt
.getLangOpts(),
57 Buffer
.getBufferStart(), SM
.getCharacterData(LocEnd
) + 1,
58 Buffer
.getBufferEnd());
59 if (Lexer
.LexFromRawLexer(Token
))
62 unsigned BaseIndent
= SM
.getSpellingColumnNumber(Statement
->getBeginLoc());
63 unsigned NewTokenIndent
= SM
.getSpellingColumnNumber(Token
.getLocation());
64 unsigned NewTokenLine
= SM
.getSpellingLineNumber(Token
.getLocation());
66 if (!IsIfStmt
&& NewTokenIndent
<= BaseIndent
&&
67 Token
.getKind() != tok::l_brace
&& NewTokenLine
!= SemicolonLine
)
70 diag(LocStart
, "potentially unintended semicolon")
71 << FixItHint::CreateRemoval(SourceRange(LocStart
, LocEnd
));
74 } // namespace clang::tidy::bugprone