File: BlinkDataMemberTypeChecker.cpp

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (159 lines) | stat: -rw-r--r-- 5,971 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "BlinkDataMemberTypeChecker.h"

#include "Util.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Diagnostic.h"

using namespace clang;

namespace chrome_checker {

BlinkDataMemberTypeChecker::BlinkDataMemberTypeChecker(
    CompilerInstance& instance)
    : instance_(instance),
      diagnostic_(instance.getDiagnostics()),
      discouraged_types_({
          {"GURL", "KURL"},
          {"std::deque", "WTF::Deque"},
          {"std::map", "WTF::HashMap or WTF::LinkedHashSet"},
          {"std::multimap",
           "WTF::HashMap<K, WTF::Vector<V>> or WTF::HashCountedSet<T>"},
          {"std::multiset", "WTF::HashCountedSet<T>"},
          {"std::set", "WTF::HashSet or WTF::LinkedHashSet"},
          {"std::unordered_set", "WTF::HashSet"},
          {"std::unordered_map", "WTF::HashMap"},
          {"std::vector", "WTF::Vector"},
      }),
      included_filenames_regex_("/third_party/blink/renderer/"),
      excluded_filenames_regex_(
          "_(unit|perf)?test\\."
          "|_fuzzer\\."
          "|/testing/"
          "|/tests/"
          "|_test_helpers") {
  auto error_level = diagnostic_.getWarningsAsErrors()
                         ? DiagnosticsEngine::Error
                         : DiagnosticsEngine::Warning;

  diag_disallowed_blink_data_member_type_ = diagnostic_.getCustomDiagID(
      error_level,
      "[blink-style] '%0' is discouraged for data members in blink renderer. "
      "Use %1 if possible. If the usage is necessary, add "
      "ALLOW_DISCOURAGED_TYPE(reason) to the data member or the type alias to "
      "suppress this message.");
}

void BlinkDataMemberTypeChecker::CheckClass(SourceLocation location,
                                            const CXXRecordDecl* record) {
  std::string filename = GetFilename(instance_.getSourceManager(), location,
                                     FilenameLocationType::kSpellingLoc);
  if (!included_filenames_regex_.match(filename))
    return;
  if (excluded_filenames_regex_.match(filename))
    return;

  for (auto* field : record->fields())
    CheckField(field);
}

bool BlinkDataMemberTypeChecker::AllowsDiscouragedType(const Decl* decl) {
  for (auto* attr : decl->attrs()) {
    if (auto* annotate = dyn_cast<AnnotateAttr>(attr)) {
      if (annotate->getAnnotation() == "allow_discouraged_type")
        return true;
    }
  }
  return false;
}

void BlinkDataMemberTypeChecker::CheckField(const FieldDecl* field) {
  if (AllowsDiscouragedType(field))
    return;

  const Type* type = field->getType().getTypePtr();
  while (type) {
    if (auto* array = dyn_cast<ArrayType>(type)) {
      // Find the element type of the array type.
      type = array->getElementType().getTypePtr();
      continue;
    }
    if (auto* elaborated = dyn_cast<ElaboratedType>(type)) {
      // Find the underlying type of the elaborated type. E.g. for
      // |TypeName v;| where |TypeName| is not a built-in type, v's type is an
      // elaborated type enclosing the actual type named |TypeName|. Though
      // getAsCXXRecordDecl() of this type can return the record decl of the
      // root underlying type directly, we want to desugar the types
      // step-by-step to check the intermediate typedef types.
      type = elaborated->getNamedType().getTypePtr();
      continue;
    }

    const NamedDecl* decl = nullptr;
    if (auto* type_def = dyn_cast<TypedefType>(type)) {
      decl = type_def->getDecl();
      // We will either break the loop below if the type name is not under blink
      // namespace, or continue to check the underlying type of typedef/using.
      type = type_def->desugar().getTypePtr();
    } else if (auto* spec = dyn_cast<TemplateSpecializationType>(type)) {
      // Check cases like "std::vector<T> v;" in a template.
      decl = spec->getTemplateName().getAsTemplateDecl();
      // We may continue if the type still have an underlying type, like the
      // typedef case.
      type = spec->isSugared() ? spec->desugar().getTypePtr() : nullptr;
    } else {
      // For other kinds of types, get the root underlying type directly.
      decl = type->getAsCXXRecordDecl();
      // This will break the loop as we have found the root underlying type.
      type = nullptr;
    }

    if (!decl || AllowsDiscouragedType(decl))
      return;

    std::string type_name = decl->getQualifiedNameAsString();
    auto it = discouraged_types_.find(type_name);
    if (it != discouraged_types_.end()) {
      diagnostic_.Report(field->getLocation(),
                         diag_disallowed_blink_data_member_type_)
          << type_name << it->second;
      return;
    }

    // Skip the following conditions if we will break the loop anyway.
    if (!type)
      return;

    // Stop if the underlying type is not under blink namespace, instead of
    // finding the root underlying type. This is to allow the following case:
    // namespace cc {
    //   using LayerList = std::vector<Layer*>;
    // }
    // namespace blink {
    //   class LayerBuilder {
    //     ...
    //     // This is allowed as long as cc::LayerList is allowed in
    //     // audit_non_blink_usages.py.
    //     cc::LayerList layer_list_;
    //   };
    // Finding the root underlying type would disallow the above usage.
    if (type_name.find("blink::") != 0)
      return;

    // Similarly, stop finding the root underlying type if the intermediate
    // type is defined in a file that should not be checked, e.g. in a file
    // under third_party/blink/public/common.
    std::string filename =
        GetFilename(instance_.getSourceManager(), decl->getLocation(),
                    FilenameLocationType::kSpellingLoc);
    if (!included_filenames_regex_.match(filename))
      return;
  }
}

}  // namespace chrome_checker