brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.7 KiB · ff866e9 Raw
173 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 "StaticAssertCheck.h"10#include "../utils/Matchers.h"11#include "clang/AST/ASTContext.h"12#include "clang/AST/Expr.h"13#include "clang/ASTMatchers/ASTMatchFinder.h"14#include "clang/Frontend/CompilerInstance.h"15#include "clang/Lex/Lexer.h"16#include "llvm/ADT/StringRef.h"17#include <optional>18#include <string>19 20using namespace clang::ast_matchers;21 22namespace clang::tidy::misc {23 24StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context)25    : ClangTidyCheck(Name, Context) {}26 27void StaticAssertCheck::registerMatchers(MatchFinder *Finder) {28  auto NegatedString = unaryOperator(29      hasOperatorName("!"), hasUnaryOperand(ignoringImpCasts(stringLiteral())));30  auto IsAlwaysFalse =31      expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),32                 cxxNullPtrLiteralExpr(), gnuNullExpr(), NegatedString))33          .bind("isAlwaysFalse");34  auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(35      IsAlwaysFalse, cStyleCastExpr(has(ignoringParenImpCasts(IsAlwaysFalse)))36                         .bind("castExpr")));37  auto AssertExprRoot = anyOf(38      binaryOperator(39          hasAnyOperatorName("&&", "=="),40          hasEitherOperand(ignoringImpCasts(stringLiteral().bind("assertMSG"))),41          optionally(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast))))42          .bind("assertExprRoot"),43      IsAlwaysFalse);44  auto NonConstexprFunctionCall =45      callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));46  auto NonConstexprVariableReference =47      declRefExpr(to(varDecl(unless(isConstexpr()))),48                  unless(hasAncestor(expr(matchers::hasUnevaluatedContext()))),49                  unless(hasAncestor(typeLoc())));50 51  auto NonConstexprCode =52      expr(anyOf(NonConstexprFunctionCall, NonConstexprVariableReference));53  auto AssertCondition =54      expr(optionally(expr(ignoringParenCasts(anyOf(55               AssertExprRoot, unaryOperator(hasUnaryOperand(56                                   ignoringParenCasts(AssertExprRoot))))))),57           unless(NonConstexprCode), unless(hasDescendant(NonConstexprCode)))58          .bind("condition");59  auto Condition =60      anyOf(ignoringParenImpCasts(callExpr(61                hasDeclaration(functionDecl(hasName("__builtin_expect"))),62                hasArgument(0, AssertCondition))),63            AssertCondition);64 65  Finder->addMatcher(conditionalOperator(hasCondition(Condition),66                                         unless(isInTemplateInstantiation()))67                         .bind("condStmt"),68                     this);69 70  Finder->addMatcher(71      ifStmt(hasCondition(Condition), unless(isInTemplateInstantiation()))72          .bind("condStmt"),73      this);74}75 76void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) {77  const ASTContext *ASTCtx = Result.Context;78  const LangOptions &Opts = ASTCtx->getLangOpts();79  const SourceManager &SM = ASTCtx->getSourceManager();80  const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>("condStmt");81  const auto *Condition = Result.Nodes.getNodeAs<Expr>("condition");82  const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>("isAlwaysFalse");83  const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>("assertMSG");84  const auto *AssertExprRoot =85      Result.Nodes.getNodeAs<BinaryOperator>("assertExprRoot");86  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("castExpr");87  const SourceLocation AssertExpansionLoc = CondStmt->getBeginLoc();88 89  if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID())90    return;91 92  const StringRef MacroName =93      Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts);94 95  if (MacroName != "assert" || Condition->isValueDependent() ||96      Condition->isTypeDependent() || Condition->isInstantiationDependent() ||97      !Condition->isEvaluatable(*ASTCtx))98    return;99 100  // False literal is not the result of macro expansion.101  if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) {102    const SourceLocation FalseLiteralLoc =103        SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc());104    if (!FalseLiteralLoc.isMacroID())105      return;106 107    const StringRef FalseMacroName =108        Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts);109    if (FalseMacroName.compare_insensitive("false") == 0 ||110        FalseMacroName.compare_insensitive("null") == 0)111      return;112  }113 114  const SourceLocation AssertLoc =115      SM.getImmediateMacroCallerLoc(AssertExpansionLoc);116 117  SmallVector<FixItHint, 4> FixItHints;118  SourceLocation LastParenLoc;119  if (AssertLoc.isValid() && !AssertLoc.isMacroID() &&120      (LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).isValid()) {121    FixItHints.push_back(122        FixItHint::CreateReplacement(SourceRange(AssertLoc), "static_assert"));123 124    if (AssertExprRoot) {125      FixItHints.push_back(FixItHint::CreateRemoval(126          SourceRange(AssertExprRoot->getOperatorLoc())));127      FixItHints.push_back(FixItHint::CreateRemoval(128          SourceRange(AssertMSG->getBeginLoc(), AssertMSG->getEndLoc())));129      FixItHints.push_back(FixItHint::CreateInsertion(130          LastParenLoc, (Twine(", \"") + AssertMSG->getString() + "\"").str()));131    } else if (!Opts.CPlusPlus17) {132      FixItHints.push_back(FixItHint::CreateInsertion(LastParenLoc, ", \"\""));133    }134  }135 136  diag(AssertLoc, "found assert() that could be replaced by static_assert()")137      << FixItHints;138}139 140SourceLocation StaticAssertCheck::getLastParenLoc(const ASTContext *ASTCtx,141                                                  SourceLocation AssertLoc) {142  const LangOptions &Opts = ASTCtx->getLangOpts();143  const SourceManager &SM = ASTCtx->getSourceManager();144 145  std::optional<llvm::MemoryBufferRef> Buffer =146      SM.getBufferOrNone(SM.getFileID(AssertLoc));147  if (!Buffer)148    return {};149 150  const char *BufferPos = SM.getCharacterData(AssertLoc);151 152  Token Token;153  Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts,154              Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());155 156  //        assert                          first left parenthesis157  if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) ||158      !Token.is(tok::l_paren))159    return {};160 161  unsigned int ParenCount = 1;162  while (ParenCount && !Lexer.LexFromRawLexer(Token)) {163    if (Token.is(tok::l_paren))164      ++ParenCount;165    else if (Token.is(tok::r_paren))166      --ParenCount;167  }168 169  return Token.getLocation();170}171 172} // namespace clang::tidy::misc173