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