76 lines · cpp
1//===----------------------------------------------------------------------===//2//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "SuspiciousSemicolonCheck.h"10#include "../utils/LexerUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13 14using namespace clang::ast_matchers;15 16namespace clang::tidy::bugprone {17 18void SuspiciousSemicolonCheck::registerMatchers(MatchFinder *Finder) {19 Finder->addMatcher(20 stmt(anyOf(ifStmt(hasThen(nullStmt().bind("semi")),21 unless(hasElse(stmt())), unless(isConstexpr())),22 forStmt(hasBody(nullStmt().bind("semi"))),23 cxxForRangeStmt(hasBody(nullStmt().bind("semi"))),24 whileStmt(hasBody(nullStmt().bind("semi")))))25 .bind("stmt"),26 this);27}28 29void SuspiciousSemicolonCheck::check(const MatchFinder::MatchResult &Result) {30 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())31 return;32 33 const auto *Semicolon = Result.Nodes.getNodeAs<NullStmt>("semi");34 const SourceLocation LocStart = Semicolon->getBeginLoc();35 36 if (LocStart.isMacroID())37 return;38 39 ASTContext &Ctxt = *Result.Context;40 auto Token = utils::lexer::getPreviousToken(LocStart, Ctxt.getSourceManager(),41 Ctxt.getLangOpts());42 auto &SM = *Result.SourceManager;43 const unsigned SemicolonLine = SM.getSpellingLineNumber(LocStart);44 45 const auto *Statement = Result.Nodes.getNodeAs<Stmt>("stmt");46 const bool IsIfStmt = isa<IfStmt>(Statement);47 48 if (!IsIfStmt &&49 SM.getSpellingLineNumber(Token.getLocation()) != SemicolonLine)50 return;51 52 const SourceLocation LocEnd = Semicolon->getEndLoc();53 const FileID FID = SM.getFileID(LocEnd);54 const llvm::MemoryBufferRef Buffer = SM.getBufferOrFake(FID, LocEnd);55 Lexer Lexer(SM.getLocForStartOfFile(FID), Ctxt.getLangOpts(),56 Buffer.getBufferStart(), SM.getCharacterData(LocEnd) + 1,57 Buffer.getBufferEnd());58 if (Lexer.LexFromRawLexer(Token))59 return;60 61 const unsigned BaseIndent =62 SM.getSpellingColumnNumber(Statement->getBeginLoc());63 const unsigned NewTokenIndent =64 SM.getSpellingColumnNumber(Token.getLocation());65 const unsigned NewTokenLine = SM.getSpellingLineNumber(Token.getLocation());66 67 if (!IsIfStmt && NewTokenIndent <= BaseIndent &&68 Token.getKind() != tok::l_brace && NewTokenLine != SemicolonLine)69 return;70 71 diag(LocStart, "potentially unintended semicolon")72 << FixItHint::CreateRemoval(SourceRange(LocStart, LocEnd));73}74 75} // namespace clang::tidy::bugprone76