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