StaticAssertCheck.cpp 6.4 KB

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