10#include "clang/AST/ASTContext.h"
11#include "clang/AST/ParentMapContext.h"
12#include "clang/AST/RecursiveASTVisitor.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
22AST_MATCHER(CXXMethodDecl, isStatic) {
return Node.isStatic(); }
24AST_MATCHER(CXXMethodDecl, hasTrivialBody) {
return Node.hasTrivialBody(); }
27 return Node.hasAnyDependentBases();
31 return Node.getTemplatedKind() != FunctionDecl::TK_NonTemplate;
35 return Node.isDependentContext();
38AST_MATCHER(CXXMethodDecl, isInsideMacroDefinition) {
39 const ASTContext &Ctxt = Finder->getASTContext();
40 return clang::Lexer::makeFileCharRange(
41 clang::CharSourceRange::getCharRange(
42 Node.getTypeSourceInfo()->getTypeLoc().getSourceRange()),
43 Ctxt.getSourceManager(), Ctxt.getLangOpts())
48 ast_matchers::internal::Matcher<CXXMethodDecl>, InnerMatcher) {
49 return InnerMatcher.matches(*Node.getCanonicalDecl(), Finder, Builder);
52enum UsageKind { Unused, Const, NonConst };
54class FindUsageOfThis :
public RecursiveASTVisitor<FindUsageOfThis> {
58 FindUsageOfThis(ASTContext &Ctxt) : Ctxt(Ctxt) {}
59 UsageKind Usage = Unused;
61 template <
class T>
const T *getParent(
const Expr *E) {
62 DynTypedNodeList
Parents = Ctxt.getParents(*E);
69 const Expr *getParentExprIgnoreParens(
const Expr *E) {
70 const Expr *Parent = getParent<Expr>(E);
71 while (isa_and_nonnull<ParenExpr>(Parent))
72 Parent = getParent<Expr>(Parent);
76 bool VisitUnresolvedMemberExpr(
const UnresolvedMemberExpr *) {
83 bool VisitCXXConstCastExpr(
const CXXConstCastExpr *) {
101 bool visitUser(
const ImplicitCastExpr *Cast) {
102 if (Cast->getCastKind() != CK_NoOp)
106 QualType QT = Cast->getType();
107 if (QT->isPointerType())
108 QT = QT->getPointeeType();
110 if (!QT.isConstQualified())
113 const auto *Parent = getParent<Stmt>(Cast);
117 if (isa<ReturnStmt>(Parent))
120 if (isa<CallExpr>(Parent))
124 if (
const auto *Member = dyn_cast<MemberExpr>(Parent))
125 return visitUser(Member,
true);
132 bool visitUser(
const MemberExpr *Member,
bool OnConstObject) {
133 if (Member->isBoundMemberFunction(Ctxt)) {
134 if (!OnConstObject || Member->getFoundDecl().getAccess() != AS_public) {
150 const auto *Parent = getParentExprIgnoreParens(Member);
152 if (
const auto *Cast = dyn_cast_or_null<ImplicitCastExpr>(Parent)) {
157 if (Member->getFoundDecl().getAccess() != AS_public &&
158 !Cast->getType()->isBuiltinType())
161 if (Cast->getCastKind() == CK_LValueToRValue)
164 if (Cast->getCastKind() == CK_NoOp && Cast->getType().isConstQualified())
168 if (
const auto *M = dyn_cast_or_null<MemberExpr>(Parent))
169 return visitUser(M,
false);
174 bool VisitCXXThisExpr(
const CXXThisExpr *E) {
177 const auto *Parent = getParentExprIgnoreParens(E);
180 if (
const auto *UnOp = dyn_cast_or_null<UnaryOperator>(Parent)) {
181 if (UnOp->getOpcode() == UO_Deref) {
182 Parent = getParentExprIgnoreParens(UnOp);
191 if (
const auto *Cast = dyn_cast_or_null<ImplicitCastExpr>(Parent)) {
199 }
else if (
const auto *Member = dyn_cast_or_null<MemberExpr>(Parent)) {
200 if (visitUser(Member,
false))
211 FindUsageOfThis UsageOfThis(Finder->getASTContext());
214 UsageOfThis.TraverseStmt(Node.getBody());
216 return UsageOfThis.Usage == Const;
226 isDefinition(), isUserProvided(),
228 isExpansionInSystemHeader(), isVirtual(), isConst(),
229 isStatic(), hasTrivialBody(), cxxConstructorDecl(),
230 cxxDestructorDecl(), isTemplate(), isDependentContext(),
231 ofClass(anyOf(isLambda(),
232 hasAnyDependentBases())
236 isInsideMacroDefinition(),
237 hasCanonicalDecl(isInsideMacroDefinition()))),
244 TypeSourceInfo *TSI = M->getTypeSourceInfo();
248 auto FTL = TSI->getTypeLoc().IgnoreParens().getAs<FunctionTypeLoc>();
252 return FTL.getRParenLoc().getLocWithOffset(1);
256 const MatchFinder::MatchResult &Result) {
257 const auto *Definition = Result.Nodes.getNodeAs<CXXMethodDecl>(
"x");
259 const auto *Declaration = Definition->getCanonicalDecl();
261 auto Diag = diag(Definition->getLocation(),
"method %0 can be made const")
265 if (Declaration != Definition) {
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)
static SourceLocation getConstInsertionPoint(const CXXMethodDecl *M)