blob: 5cbe91cc9cf8e68023a22ee5e534d4c8e9f14909 [file] [log] [blame]
Avi Drissmandfd880852022-09-15 20:11:091// Copyright 2018 The Chromium Authors
Daniel Cheng657bf202018-04-26 21:50:022// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "Util.h"
6
miktdfd2cae2023-05-23 04:10:577#include <algorithm>
8
Daniel Cheng657bf202018-04-26 21:50:029#include "clang/AST/Decl.h"
Xianzhu Wang86b11462022-09-24 22:00:1210#include "clang/Basic/SourceManager.h"
danakjc06d5f42024-02-29 17:54:1111#include "llvm/ADT/StringRef.h"
Daniel Cheng657bf202018-04-26 21:50:0212#include "llvm/Support/Casting.h"
13#include "llvm/Support/raw_ostream.h"
14
15namespace {
16
danakjc06d5f42024-02-29 17:54:1117// Directories which are treated as third-party code, which can be used to
18// prevent emitting diagnostics in them.
19//
20// Each one must start and end with a `/` to be used correctly.
21const char* kTreatAsThirdPartyDirs[] = {
22 "/breakpad/", //
23 "/courgette/", //
24 "/frameworks/", //
25 "/native_client/", //
26 "/ppapi/", //
27 "/testing/", //
28 "/v8/", //
29};
30
Daniel Cheng657bf202018-04-26 21:50:0231std::string GetNamespaceImpl(const clang::DeclContext* context,
32 const std::string& candidate) {
33 switch (context->getDeclKind()) {
34 case clang::Decl::TranslationUnit: {
35 return candidate;
36 }
37 case clang::Decl::Namespace: {
38 const auto* decl = llvm::dyn_cast<clang::NamespaceDecl>(context);
39 std::string name_str;
40 llvm::raw_string_ostream OS(name_str);
danakjc06d5f42024-02-29 17:54:1141 if (decl->isAnonymousNamespace()) {
Daniel Cheng657bf202018-04-26 21:50:0242 OS << "<anonymous namespace>";
danakjc06d5f42024-02-29 17:54:1143 } else {
Daniel Cheng657bf202018-04-26 21:50:0244 OS << *decl;
danakjc06d5f42024-02-29 17:54:1145 }
Daniel Cheng657bf202018-04-26 21:50:0246 return GetNamespaceImpl(context->getParent(), OS.str());
47 }
danakjc06d5f42024-02-29 17:54:1148 default: {
49 return GetNamespaceImpl(context->getParent(), candidate);
50 }
Daniel Cheng657bf202018-04-26 21:50:0251 }
52}
53
54} // namespace
55
56std::string GetNamespace(const clang::Decl* record) {
57 return GetNamespaceImpl(record->getDeclContext(), std::string());
58}
Xianzhu Wang86b11462022-09-24 22:00:1259
Keishi Hattori615f8fa2022-11-07 23:50:1460std::string GetFilename(const clang::SourceManager& source_manager,
danakj69209bf2024-04-19 14:51:0561 clang::SourceLocation loc,
62 FilenameLocationType type,
danakje7db1e3f32024-04-16 20:43:2463 FilenamesFollowPresumed follow_presumed) {
danakj69209bf2024-04-19 14:51:0564 switch (type) {
65 case FilenameLocationType::kExactLoc:
66 break;
67 case FilenameLocationType::kSpellingLoc:
68 loc = source_manager.getSpellingLoc(loc);
69 break;
70 case FilenameLocationType::kExpansionLoc:
71 loc = source_manager.getExpansionLoc(loc);
72 break;
73 }
danakje7db1e3f32024-04-16 20:43:2474 std::string name;
75 if (follow_presumed == FilenamesFollowPresumed::kYes) {
danakj69209bf2024-04-19 14:51:0576 clang::PresumedLoc ploc = source_manager.getPresumedLoc(loc);
danakje7db1e3f32024-04-16 20:43:2477 if (ploc.isInvalid()) {
78 // If we're in an invalid location, we're looking at things that aren't
79 // actually stated in the source.
80 return name;
81 }
82 name = ploc.getFilename();
83 } else {
danakj69209bf2024-04-19 14:51:0584 name = source_manager.getFilename(loc);
danakje7db1e3f32024-04-16 20:43:2485 }
miktdfd2cae2023-05-23 04:10:5786
87 // File paths can have separators which differ from ones at this platform.
88 // Make them consistent.
89 std::replace(name.begin(), name.end(), '\\', '/');
90 return name;
Xianzhu Wang86b11462022-09-24 22:00:1291}
danakjc06d5f42024-02-29 17:54:1192
93namespace chrome_checker {
94
danakje7db1e3f32024-04-16 20:43:2495LocationClassification ClassifySourceLocation(
96 const clang::HeaderSearchOptions& search,
97 const clang::SourceManager& sm,
98 clang::SourceLocation loc) {
danakjc06d5f42024-02-29 17:54:1199 if (sm.isInSystemHeader(loc)) {
100 return LocationClassification::kSystem;
101 }
102
danakj69209bf2024-04-19 14:51:05103 std::string filename = GetFilename(sm, loc, FilenameLocationType::kExactLoc);
danakjc06d5f42024-02-29 17:54:11104 if (filename.empty()) {
105 // If the filename cannot be determined, simply treat this as third-party
106 // code, where we avoid enforcing rules, instead of going through the full
107 // lookup process.
108 return LocationClassification::kThirdParty;
109 }
110
danakje7db1e3f32024-04-16 20:43:24111 // Files in the sysroot do not automatically get categorized as system
112 // headers, so we do a path comparison. The sysroot can be set to "/" when it
113 // was not specified, which is just the whole filesystem, but every file is
114 // not a system header, so this is treated as equivalent to not having a
115 // sysroot.
116 if (!search.Sysroot.empty() && search.Sysroot != "/" &&
117 llvm::StringRef(filename).starts_with(search.Sysroot)) {
118 return LocationClassification::kSystem;
119 }
120
Tom Sepez01e8ba0e2025-01-27 18:51:48121 // The Win SDK headers don't get categorized as system headers when building
122 // with DEPOT_TOOLS_WIN_TOOLCHAIN=0.
123 if (filename.find("Program Files (x86)/Windows Kits/") != std::string::npos) {
124 return LocationClassification::kSystem;
125 }
126
danakjc06d5f42024-02-29 17:54:11127 // We need to special case scratch space; which is where clang does its macro
128 // expansion. We explicitly want to allow people to do otherwise bad things
129 // through macros that were defined due to third party libraries.
130 //
131 // TODO(danakj): We can further classify this as first/third-party code using
132 // a macro defined in first/third-party code. See
133 // https://github.com/chromium/subspace/blob/f9c481a241961a7be827d31fadb01badac6ee86a/subdoc/lib/visit.cc#L1566-L1577
134 if (filename == "<scratch space>") {
135 return LocationClassification::kMacro;
136 }
137
138 // Ensure that we can search for patterns of the form "/foo/" even
139 // if we have a relative path like "foo/bar.cc". We don't expect
140 // this transformed path to exist necessarily.
141 if (filename.front() != '/') {
142 filename.insert(0, 1, '/');
143 }
144
145 if (filename.find("/gen/") != std::string::npos) {
146 return LocationClassification::kGenerated;
147 }
148
149 // While blink is inside third_party, it's not all treated like third-party
150 // code.
151 if (auto p = filename.find("/third_party/blink/"); p != std::string::npos) {
152 // Browser-side code is treated like first party in order to have all
153 // diagnostics applied. Over time we want the rest of blink code to
154 // converge as well.
155 //
156 // TODO(danakj): Use starts_with() when Clang is compiled with C++20.
157 if (!llvm::StringRef(filename).substr(p).starts_with("browser/")) {
158 return LocationClassification::kBlink;
159 }
160 }
161
162 if (filename.find("/third_party/") != std::string::npos) {
163 return LocationClassification::kThirdParty;
164 }
165
166 for (const char* dir : kTreatAsThirdPartyDirs) {
167 if (filename.find(dir) != std::string::npos) {
168 return LocationClassification::kThirdParty;
169 }
170 }
171
172 // TODO(danakj): Designate chromium-owned code in third_party as
173 // kChromiumFirstParty.
174 return LocationClassification::kFirstParty;
175}
176
177} // namespace chrome_checker