clang-tools  3.8.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 
26 StaticAssertCheck::StaticAssertCheck(StringRef Name, ClangTidyContext *Context)
27  : ClangTidyCheck(Name, Context) {}
28 
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 IsAlwaysFalse = expr(ignoringParenImpCasts(
36  expr(anyOf(cxxBoolLiteral(equals(false)), integerLiteral(equals(0)),
37  cxxNullPtrLiteralExpr(), gnuNullExpr()))
38  .bind("isAlwaysFalse")));
39  auto IsAlwaysFalseWithCast = ignoringParenImpCasts(anyOf(
40  IsAlwaysFalse, cStyleCastExpr(has(IsAlwaysFalse)).bind("castExpr")));
41  auto AssertExprRoot = anyOf(
42  binaryOperator(
43  anyOf(hasOperatorName("&&"), hasOperatorName("==")),
44  hasEitherOperand(ignoringImpCasts(stringLiteral().bind("assertMSG"))),
45  anyOf(binaryOperator(hasEitherOperand(IsAlwaysFalseWithCast)),
46  anything()))
47  .bind("assertExprRoot"),
48  IsAlwaysFalse);
49  auto NonConstexprFunctionCall =
50  callExpr(hasDeclaration(functionDecl(unless(isConstexpr()))));
51  auto AssertCondition =
52  expr(
53  anyOf(expr(ignoringParenCasts(anyOf(
54  AssertExprRoot, unaryOperator(hasUnaryOperand(
55  ignoringParenCasts(AssertExprRoot)))))),
56  anything()),
57  unless(findAll(NonConstexprFunctionCall)))
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(stmt(anyOf(conditionalOperator(hasCondition(Condition)),
66  ifStmt(hasCondition(Condition))),
67  unless(isInTemplateInstantiation()))
68  .bind("condStmt"),
69  this);
70 }
71 
72 void StaticAssertCheck::check(const MatchFinder::MatchResult &Result) {
73  const ASTContext *ASTCtx = Result.Context;
74  const LangOptions &Opts = ASTCtx->getLangOpts();
75  const SourceManager &SM = ASTCtx->getSourceManager();
76  const auto *CondStmt = Result.Nodes.getNodeAs<Stmt>("condStmt");
77  const auto *Condition = Result.Nodes.getNodeAs<Expr>("condition");
78  const auto *IsAlwaysFalse = Result.Nodes.getNodeAs<Expr>("isAlwaysFalse");
79  const auto *AssertMSG = Result.Nodes.getNodeAs<StringLiteral>("assertMSG");
80  const auto *AssertExprRoot =
81  Result.Nodes.getNodeAs<BinaryOperator>("assertExprRoot");
82  const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("castExpr");
83  SourceLocation AssertExpansionLoc = CondStmt->getLocStart();
84 
85  if (!AssertExpansionLoc.isValid() || !AssertExpansionLoc.isMacroID())
86  return;
87 
88  StringRef MacroName =
89  Lexer::getImmediateMacroName(AssertExpansionLoc, SM, Opts);
90 
91  if (MacroName != "assert" || Condition->isValueDependent() ||
92  Condition->isTypeDependent() || Condition->isInstantiationDependent() ||
93  !Condition->isEvaluatable(*ASTCtx))
94  return;
95 
96  // False literal is not the result of macro expansion.
97  if (IsAlwaysFalse && (!CastExpr || CastExpr->getType()->isPointerType())) {
98  SourceLocation FalseLiteralLoc =
99  SM.getImmediateSpellingLoc(IsAlwaysFalse->getExprLoc());
100  if (!FalseLiteralLoc.isMacroID())
101  return;
102 
103  StringRef FalseMacroName =
104  Lexer::getImmediateMacroName(FalseLiteralLoc, SM, Opts);
105  if (FalseMacroName.compare_lower("false") == 0 ||
106  FalseMacroName.compare_lower("null") == 0)
107  return;
108  }
109 
110  SourceLocation AssertLoc = SM.getImmediateMacroCallerLoc(AssertExpansionLoc);
111 
112  SmallVector<FixItHint, 4> FixItHints;
113  SourceLocation LastParenLoc;
114  if (AssertLoc.isValid() && !AssertLoc.isMacroID() &&
115  (LastParenLoc = getLastParenLoc(ASTCtx, AssertLoc)).isValid()) {
116  FixItHints.push_back(
117  FixItHint::CreateReplacement(SourceRange(AssertLoc), "static_assert"));
118 
119  std::string StaticAssertMSG = ", \"\"";
120  if (AssertExprRoot) {
121  FixItHints.push_back(FixItHint::CreateRemoval(
122  SourceRange(AssertExprRoot->getOperatorLoc())));
123  FixItHints.push_back(FixItHint::CreateRemoval(
124  SourceRange(AssertMSG->getLocStart(), AssertMSG->getLocEnd())));
125  StaticAssertMSG = (Twine(", \"") + AssertMSG->getString() + "\"").str();
126  }
127 
128  FixItHints.push_back(
129  FixItHint::CreateInsertion(LastParenLoc, StaticAssertMSG));
130  }
131 
132  diag(AssertLoc, "found assert() that could be replaced by static_assert()")
133  << FixItHints;
134 }
135 
136 SourceLocation StaticAssertCheck::getLastParenLoc(const ASTContext *ASTCtx,
137  SourceLocation AssertLoc) {
138  const LangOptions &Opts = ASTCtx->getLangOpts();
139  const SourceManager &SM = ASTCtx->getSourceManager();
140 
141  llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getFileID(AssertLoc));
142  if (!Buffer)
143  return SourceLocation();
144 
145  const char *BufferPos = SM.getCharacterData(AssertLoc);
146 
147  Token Token;
148  Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(AssertLoc)), Opts,
149  Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
150 
151  // assert first left parenthesis
152  if (Lexer.LexFromRawLexer(Token) || Lexer.LexFromRawLexer(Token) ||
153  !Token.is(tok::l_paren))
154  return SourceLocation();
155 
156  unsigned int ParenCount = 1;
157  while (ParenCount && !Lexer.LexFromRawLexer(Token)) {
158  if (Token.is(tok::l_paren))
159  ++ParenCount;
160  else if (Token.is(tok::r_paren))
161  --ParenCount;
162  }
163 
164  return Token.getLocation();
165 }
166 
167 } // namespace tidy
168 } // namespace clang
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:162
StringHandle Name
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
Base class for all clang-tidy checks.
Definition: ClangTidy.h:102
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
SourceManager & SM
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
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's name.
Definition: ClangTidy.cpp:323
const NamedDecl * Result
Definition: USRFinder.cpp:121