1*f329e3edSDiscookie //===--- PointerArithmeticOnPolymorphicObjectCheck.cpp - clang-tidy--------===//
2*f329e3edSDiscookie //
3*f329e3edSDiscookie // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*f329e3edSDiscookie // See https://llvm.org/LICENSE.txt for license information.
5*f329e3edSDiscookie // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*f329e3edSDiscookie //
7*f329e3edSDiscookie //===----------------------------------------------------------------------===//
8*f329e3edSDiscookie
9*f329e3edSDiscookie #include "PointerArithmeticOnPolymorphicObjectCheck.h"
10*f329e3edSDiscookie #include "clang/AST/ASTContext.h"
11*f329e3edSDiscookie #include "clang/ASTMatchers/ASTMatchFinder.h"
12*f329e3edSDiscookie
13*f329e3edSDiscookie using namespace clang::ast_matchers;
14*f329e3edSDiscookie
15*f329e3edSDiscookie namespace clang::tidy::bugprone {
16*f329e3edSDiscookie
17*f329e3edSDiscookie namespace {
AST_MATCHER(CXXRecordDecl,isAbstract)18*f329e3edSDiscookie AST_MATCHER(CXXRecordDecl, isAbstract) { return Node.isAbstract(); }
AST_MATCHER(CXXRecordDecl,isPolymorphic)19*f329e3edSDiscookie AST_MATCHER(CXXRecordDecl, isPolymorphic) { return Node.isPolymorphic(); }
20*f329e3edSDiscookie } // namespace
21*f329e3edSDiscookie
22*f329e3edSDiscookie PointerArithmeticOnPolymorphicObjectCheck::
PointerArithmeticOnPolymorphicObjectCheck(StringRef Name,ClangTidyContext * Context)23*f329e3edSDiscookie PointerArithmeticOnPolymorphicObjectCheck(StringRef Name,
24*f329e3edSDiscookie ClangTidyContext *Context)
25*f329e3edSDiscookie : ClangTidyCheck(Name, Context),
26*f329e3edSDiscookie IgnoreInheritedVirtualFunctions(
27*f329e3edSDiscookie Options.get("IgnoreInheritedVirtualFunctions", false)) {}
28*f329e3edSDiscookie
storeOptions(ClangTidyOptions::OptionMap & Opts)29*f329e3edSDiscookie void PointerArithmeticOnPolymorphicObjectCheck::storeOptions(
30*f329e3edSDiscookie ClangTidyOptions::OptionMap &Opts) {
31*f329e3edSDiscookie Options.store(Opts, "IgnoreInheritedVirtualFunctions",
32*f329e3edSDiscookie IgnoreInheritedVirtualFunctions);
33*f329e3edSDiscookie }
34*f329e3edSDiscookie
registerMatchers(MatchFinder * Finder)35*f329e3edSDiscookie void PointerArithmeticOnPolymorphicObjectCheck::registerMatchers(
36*f329e3edSDiscookie MatchFinder *Finder) {
37*f329e3edSDiscookie const auto PolymorphicPointerExpr =
38*f329e3edSDiscookie expr(hasType(hasCanonicalType(pointerType(pointee(hasCanonicalType(
39*f329e3edSDiscookie hasDeclaration(cxxRecordDecl(unless(isFinal()), isPolymorphic())
40*f329e3edSDiscookie .bind("pointee"))))))))
41*f329e3edSDiscookie .bind("pointer");
42*f329e3edSDiscookie
43*f329e3edSDiscookie const auto PointerExprWithVirtualMethod =
44*f329e3edSDiscookie expr(hasType(hasCanonicalType(
45*f329e3edSDiscookie pointerType(pointee(hasCanonicalType(hasDeclaration(
46*f329e3edSDiscookie cxxRecordDecl(
47*f329e3edSDiscookie unless(isFinal()),
48*f329e3edSDiscookie anyOf(hasMethod(isVirtualAsWritten()), isAbstract()))
49*f329e3edSDiscookie .bind("pointee"))))))))
50*f329e3edSDiscookie .bind("pointer");
51*f329e3edSDiscookie
52*f329e3edSDiscookie const auto SelectedPointerExpr = IgnoreInheritedVirtualFunctions
53*f329e3edSDiscookie ? PointerExprWithVirtualMethod
54*f329e3edSDiscookie : PolymorphicPointerExpr;
55*f329e3edSDiscookie
56*f329e3edSDiscookie const auto ArraySubscript = arraySubscriptExpr(hasBase(SelectedPointerExpr));
57*f329e3edSDiscookie
58*f329e3edSDiscookie const auto BinaryOperators =
59*f329e3edSDiscookie binaryOperator(hasAnyOperatorName("+", "-", "+=", "-="),
60*f329e3edSDiscookie hasEitherOperand(SelectedPointerExpr));
61*f329e3edSDiscookie
62*f329e3edSDiscookie const auto UnaryOperators = unaryOperator(
63*f329e3edSDiscookie hasAnyOperatorName("++", "--"), hasUnaryOperand(SelectedPointerExpr));
64*f329e3edSDiscookie
65*f329e3edSDiscookie Finder->addMatcher(ArraySubscript, this);
66*f329e3edSDiscookie Finder->addMatcher(BinaryOperators, this);
67*f329e3edSDiscookie Finder->addMatcher(UnaryOperators, this);
68*f329e3edSDiscookie }
69*f329e3edSDiscookie
check(const MatchFinder::MatchResult & Result)70*f329e3edSDiscookie void PointerArithmeticOnPolymorphicObjectCheck::check(
71*f329e3edSDiscookie const MatchFinder::MatchResult &Result) {
72*f329e3edSDiscookie const auto *PointerExpr = Result.Nodes.getNodeAs<Expr>("pointer");
73*f329e3edSDiscookie const auto *PointeeDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("pointee");
74*f329e3edSDiscookie
75*f329e3edSDiscookie diag(PointerExpr->getBeginLoc(),
76*f329e3edSDiscookie "pointer arithmetic on polymorphic object of type %0 can result in "
77*f329e3edSDiscookie "undefined behavior if the dynamic type differs from the pointer type")
78*f329e3edSDiscookie << PointeeDecl << PointerExpr->getSourceRange();
79*f329e3edSDiscookie }
80*f329e3edSDiscookie
81*f329e3edSDiscookie } // namespace clang::tidy::bugprone
82