clang-tools  10.0.0svn
StaticAssertCheck.cpp
Go to the documentation of this file.
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 
9 #include "StaticAssertCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Expr.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Frontend/CompilerInstance.h"
14 #include "clang/Lex/Lexer.h"
15 #include "llvm/ADT/SmallVector.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/Casting.h"
18 #include <string>
19 
20 using namespace clang::ast_matchers;
21 
22 namespace clang {
23 namespace tidy {
24 namespace misc {
25 
26 StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context)
27  : ClangTidyCheck(Name, Context) {}
28 
29 void StaticAssertCheck::registerMatchers(MatchFinder *Finder) {
30  // This checker only makes sense for languages that have static assertion
31  // capabilities: C++11 and C11.
32  if (!(getLangOpts().CPlusPlus11 || getLangOpts().C11))
33  return;
34 
35  auto NegatedString = unaryOperator(
36  hasOperatorName("!"), hasUnaryOperand(ignoringImpCasts(stringLiteral())));
37  auto IsAlwaysFalse =
38  expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),
39  cxxNullPtrLiteralExpr(), gnuNullExpr(), NegatedString))
40  .bind("isAlwaysFalse");
41  auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(
42  IsAlwaysFalse, cStyleCastExpr(has(ignoringParenImpCasts(IsAlwaysFalse)))
43  .bind("castExpr")));
44  auto AssertExprRoot = anyOf(
45  binaryOperator(
46  anyOf(hasOperatorName("&&"), hasOperatorName("==")),
47  hasEitherOperand(ignoringImpCasts(stringLiteral().bind("assertMSG"))),
48  anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast)),
49  anything()))
50  .bind("assertExprRoot"),
51  IsAlwaysFalse);
52  auto NonConstexprFunctionCall =
53  callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));
54  auto AssertCondition =
55  expr(
56  anyOf(expr(ignoringParenCasts(anyOf(
57  AssertExprRoot, unaryOperator(hasUnaryOperand(
58  ignoringParenCasts(AssertExprRoot)))))),
59  anything()),
60  unless(findAll(NonConstexprFunctionCall)))
61  .bind("condition");
62  auto Condition =
63  anyOf(ignoringParenImpCasts(callExpr(
64  hasDeclaration(functionDecl(hasName("__builtin_expect"))),
65  hasArgument(0, AssertCondition))),
66  AssertCondition);
67 
68  Finder->addMatcher(conditionalOperator(hasCondition(Condition),
69  unless(isInTemplateInstantiation()))
70  .bind("condStmt"),
71  this);
72 
73  Finder->addMatcher(
74  ifStmt(hasCondition(Condition), unless(isInTemplateInstantiation()))
75  .bind("condStmt"),
76  this);
77 }
78 
79 void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) {
80  const ASTContext *ASTCtx = Result.Context;
81  const LangOptions &Opts = ASTCtx->getLangOpts();
82  const SourceManager &SM = ASTCtx->getSourceManager();
83  const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>("condStmt");
84  const auto *Condition = Result.Nodes.getNodeAs<Expr>("condition");
85  const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>("isAlwaysFalse");
86  const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>("assertMSG");
87  const auto *AssertExprRoot =
88  Result.Nodes.getNodeAs<BinaryOperator>("assertExprRoot");
89  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("castExpr");
90  SourceLocation AssertExpansionLoc = CondStmt->getBeginLoc();
91 
92  if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID())
93  return;
94 
95  StringRef MacroName =
96  Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts);
97 
98  if (MacroName != "assert" || Condition->isValueDependent() ||
99  Condition->isTypeDependent() || Condition->isInstantiationDependent() ||
100  !Condition->isEvaluatable(*ASTCtx))
101  return;
102 
103  // False literal is not the result of macro expansion.
104  if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) {
105  SourceLocation FalseLiteralLoc =
106  SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc());
107  if (!FalseLiteralLoc.isMacroID())
108  return;
109 
110  StringRef FalseMacroName =
111  Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts);
112  if (FalseMacroName.compare_lower("false") == 0 ||
113  FalseMacroName.compare_lower("null") == 0)
114  return;
115  }
116 
117  SourceLocation AssertLoc = SM.getImmediateMacroCallerLoc(AssertExpansionLoc);
118 
119  SmallVector<FixItHint, 4> FixItHints;
120  SourceLocation LastParenLoc;
121  if (AssertLoc.isValid() && !AssertLoc.isMacroID() &&
122  (LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).isValid()) {
123  FixItHints.push_back(
124  FixItHint::CreateReplacement(SourceRange(AssertLoc), "static_assert"));
125 
126  std::string StaticAssertMSG = ", \"\"";
127  if (AssertExprRoot) {
128  FixItHints.push_back(FixItHint::CreateRemoval(
129  SourceRange(AssertExprRoot->getOperatorLoc())));
130  FixItHints.push_back(FixItHint::CreateRemoval(
131  SourceRange(AssertMSG->getBeginLoc(), AssertMSG->getEndLoc())));
132  StaticAssertMSG = (Twine(", \"") + AssertMSG->getString() + "\"").str();
133  }
134 
135  FixItHints.push_back(
136  FixItHint::CreateInsertion(LastParenLoc, StaticAssertMSG));
137  }
138 
139  diag(AssertLoc, "found assert() that could be replaced by static_assert()")
140  << FixItHints;
141 }
142 
143 SourceLocation StaticAssertCheck::getLastParenLoc(const ASTContext *ASTCtx,
144  SourceLocation AssertLoc) {
145  const LangOptions &Opts = ASTCtx->getLangOpts();
146  const SourceManager &SM = ASTCtx->getSourceManager();
147 
148  const llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(AssertLoc));
149  if (!Buffer)
150  return SourceLocation();
151 
152  const char *BufferPos = SM.getCharacterData(AssertLoc);
153 
154  Token Token;
155  Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts,
156  Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
157 
158  // assert first left parenthesis
159  if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) ||
160  !Token.is(tok::l_paren))
161  return SourceLocation();
162 
163  unsigned int ParenCount = 1;
164  while (ParenCount && !Lexer.LexFromRawLexer(Token)) {
165  if (Token.is(tok::l_paren))
166  ++ParenCount;
167  else if (Token.is(tok::r_paren))
168  --ParenCount;
169  }
170 
171  return Token.getLocation();
172 }
173 
174 } // namespace misc
175 } // namespace tidy
176 } // namespace clang
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static constexpr llvm::StringLiteral Name
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::string Condition
Condition used after the preprocessor directive.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.