clang-tools 6.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
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
StaticAssertCheck.h
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
clang::tidy::ClangTidyCheck::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
clang::tidy::misc::StaticAssertCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: StaticAssertCheck.cpp:80
clang::tidy::misc::StaticAssertCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: StaticAssertCheck.cpp:30
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416