blob: 414e56eb5dccd00e165779d26957a2c13798b3b0 [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinskice5e56e2016-10-21 17:56:45 -070017#include "link/ReferenceLinker.h"
18
19#include "android-base/logging.h"
20#include "androidfw/ResourceTypes.h"
21
Adam Lesinskicacb28f2016-10-19 12:18:14 -070022#include "Diagnostics.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070023#include "ResourceTable.h"
24#include "ResourceUtils.h"
25#include "ResourceValues.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070026#include "ValueVisitor.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070027#include "link/Linkers.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070028#include "process/IResourceTableConsumer.h"
29#include "process/SymbolTable.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080030#include "util/Util.h"
31#include "xml/XmlUtil.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070032
Adam Lesinskid5083f62017-01-16 15:07:21 -080033using android::StringPiece;
34
Adam Lesinski1ab598f2015-08-14 14:26:04 -070035namespace aapt {
36
37namespace {
38
39/**
Adam Lesinskicacb28f2016-10-19 12:18:14 -070040 * The ReferenceLinkerVisitor will follow all references and make sure they
41 * point
42 * to resources that actually exist, either in the local resource table, or as
43 * external
44 * symbols. Once the target resource has been found, the ID of the resource will
45 * be assigned
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046 * to the reference object.
47 *
48 * NOTE: All of the entries in the ResourceTable must be assigned IDs.
49 */
Adam Lesinski467f1712015-11-16 17:35:44 -080050class ReferenceLinkerVisitor : public ValueVisitor {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070051 public:
Adam Lesinskice5e56e2016-10-21 17:56:45 -070052 using ValueVisitor::Visit;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070053
Adam Lesinskif34b6f42017-03-03 16:33:26 -080054 ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
55 StringPool* string_pool, xml::IPackageDeclStack* decl)
56 : callsite_(callsite),
57 context_(context),
Adam Lesinskice5e56e2016-10-21 17:56:45 -070058 symbols_(symbols),
59 package_decls_(decl),
Adam Lesinskif34b6f42017-03-03 16:33:26 -080060 string_pool_(string_pool) {}
Adam Lesinskicacb28f2016-10-19 12:18:14 -070061
Adam Lesinskice5e56e2016-10-21 17:56:45 -070062 void Visit(Reference* ref) override {
Adam Lesinskif34b6f42017-03-03 16:33:26 -080063 if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070064 error_ = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -070065 }
66 }
67
68 /**
69 * We visit the Style specially because during this phase, values of
70 * attributes are
71 * all RawString values. Now that we are expected to resolve all symbols, we
72 * can
73 * lookup the attributes to find out which types are allowed for the
74 * attributes' values.
75 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -070076 void Visit(Style* style) override {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070077 if (style->parent) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070078 Visit(&style->parent.value());
Adam Lesinski1ab598f2015-08-14 14:26:04 -070079 }
80
Adam Lesinskicacb28f2016-10-19 12:18:14 -070081 for (Style::Entry& entry : style->entries) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070082 std::string err_str;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070083
Adam Lesinskicacb28f2016-10-19 12:18:14 -070084 // Transform the attribute reference so that it is using the fully
85 // qualified package
86 // name. This will also mark the reference as being able to see private
87 // resources if
88 // there was a '*' in the reference or if the package came from the
89 // private namespace.
Adam Lesinskice5e56e2016-10-21 17:56:45 -070090 Reference transformed_reference = entry.key;
91 TransformReferenceFromNamespace(package_decls_,
92 context_->GetCompilationPackage(),
93 &transformed_reference);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070094
95 // Find the attribute in the symbol table and check if it is visible from
96 // this callsite.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080097 const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
Adam Lesinskif34b6f42017-03-03 16:33:26 -080098 transformed_reference, callsite_, symbols_, &err_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -070099 if (symbol) {
100 // Assign our style key the correct ID.
101 // The ID may not exist.
102 entry.key.id = symbol->id;
103
104 // Try to convert the value to a more specific, typed value based on the
105 // attribute it is set to.
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800106 entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700107
108 // Link/resolve the final value (mostly if it's a reference).
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700109 entry.value->Accept(this);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700110
111 // Now verify that the type of this item is compatible with the
Adam Lesinski3124e7c2017-06-13 16:03:55 -0700112 // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
113 // check is fast and we avoid creating a DiagMessage when the match is successful.
114 if (!symbol->attribute->Matches(*entry.value, nullptr)) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700115 // The actual type of this item is incompatible with the attribute.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700116 DiagMessage msg(entry.key.GetSource());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700117
118 // Call the matches method again, this time with a DiagMessage so we
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800119 // fill in the actual error message.
Adam Lesinski3124e7c2017-06-13 16:03:55 -0700120 symbol->attribute->Matches(*entry.value, &msg);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700121 context_->GetDiagnostics()->Error(msg);
122 error_ = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700123 }
124
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700125 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700126 DiagMessage msg(entry.key.GetSource());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700127 msg << "style attribute '";
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800128 ReferenceLinker::WriteResourceName(&msg, entry.key, transformed_reference);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700129 msg << "' " << err_str;
130 context_->GetDiagnostics()->Error(msg);
131 error_ = true;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700132 }
133 }
134 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800135
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700136 bool HasError() { return error_; }
Adam Lesinski467f1712015-11-16 17:35:44 -0800137
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700138 private:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700139 DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700140
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700141 /**
142 * Transform a RawString value into a more specific, appropriate value, based
143 * on the
144 * Attribute. If a non RawString value is passed in, this is an identity
145 * transform.
146 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700147 std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148 const Attribute* attr) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700149 if (RawString* raw_string = ValueCast<RawString>(value.get())) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700150 std::unique_ptr<Item> transformed =
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700151 ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700152
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700153 // If we could not parse as any specific type, try a basic STRING.
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800154 if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700155 util::StringBuilder string_builder;
156 string_builder.Append(*raw_string->value);
157 if (string_builder) {
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800158 transformed = util::make_unique<String>(string_pool_->MakeRef(string_builder.ToString()));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700159 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700160 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700161
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700162 if (transformed) {
163 return transformed;
164 }
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800165 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700166 return value;
167 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700168
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800169 const CallSite& callsite_;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700170 IAaptContext* context_;
171 SymbolTable* symbols_;
172 xml::IPackageDeclStack* package_decls_;
173 StringPool* string_pool_;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700174 bool error_ = false;
175};
176
177class EmptyDeclStack : public xml::IPackageDeclStack {
178 public:
179 EmptyDeclStack() = default;
180
181 Maybe<xml::ExtractedPackage> TransformPackageAlias(
182 const StringPiece& alias,
183 const StringPiece& local_package) const override {
184 if (alias.empty()) {
Adam Lesinskid5083f62017-01-16 15:07:21 -0800185 return xml::ExtractedPackage{local_package.to_string(), true /* private */};
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700186 }
187 return {};
188 }
189
190 private:
191 DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700192};
193
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700194} // namespace
Adam Lesinski467f1712015-11-16 17:35:44 -0800195
196/**
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700197 * The symbol is visible if it is public, or if the reference to it is
198 * requesting private access
Adam Lesinski467f1712015-11-16 17:35:44 -0800199 * or if the callsite comes from the same package.
200 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700201bool ReferenceLinker::IsSymbolVisible(const SymbolTable::Symbol& symbol,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700202 const Reference& ref,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700203 const CallSite& callsite) {
204 if (!symbol.is_public && !ref.private_reference) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700205 if (ref.name) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700206 return callsite.resource.package == ref.name.value().package;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700207 } else if (ref.id && symbol.id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700208 return ref.id.value().package_id() == symbol.id.value().package_id();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700209 } else {
210 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800211 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700212 }
213 return true;
Adam Lesinski467f1712015-11-16 17:35:44 -0800214}
215
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800216const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
217 SymbolTable* symbols) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700218 if (reference.name) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800219 return symbols->FindByName(reference.name.value());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700220 } else if (reference.id) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700221 return symbols->FindById(reference.id.value());
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700222 } else {
223 return nullptr;
224 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800225}
226
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800227const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800228 const CallSite& callsite,
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800229 SymbolTable* symbols,
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800230 std::string* out_error) {
231 const SymbolTable::Symbol* symbol = ResolveSymbol(reference, symbols);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700232 if (!symbol) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700233 if (out_error) *out_error = "not found";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700234 return nullptr;
235 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800236
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800237 if (!IsSymbolVisible(*symbol, reference, callsite)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700238 if (out_error) *out_error = "is private";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700239 return nullptr;
240 }
241 return symbol;
Adam Lesinski467f1712015-11-16 17:35:44 -0800242}
243
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700244const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800245 const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
246 std::string* out_error) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800247 const SymbolTable::Symbol* symbol =
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800248 ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700249 if (!symbol) {
250 return nullptr;
251 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800252
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700253 if (!symbol->attribute) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700254 if (out_error) *out_error = "is not an attribute";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700255 return nullptr;
256 }
257 return symbol;
Adam Lesinski467f1712015-11-16 17:35:44 -0800258}
259
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800260Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800261 const CallSite& callsite,
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800262 SymbolTable* symbols,
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800263 std::string* out_error) {
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800264 const SymbolTable::Symbol* symbol =
265 ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700266 if (!symbol) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700267 return {};
268 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800269
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700270 if (!symbol->attribute) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700271 if (out_error) *out_error = "is not an attribute";
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700272 return {};
273 }
Adam Lesinskic744ae82017-05-17 19:28:38 -0700274 return xml::AaptAttribute(*symbol->attribute, symbol->id);
Adam Lesinski467f1712015-11-16 17:35:44 -0800275}
276
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700277void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700278 const Reference& orig,
Adam Lesinski28cacf02015-11-23 14:22:47 -0800279 const Reference& transformed) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700280 CHECK(out_msg != nullptr);
Adam Lesinski28cacf02015-11-23 14:22:47 -0800281
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700282 if (orig.name) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700283 *out_msg << orig.name.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700284 if (transformed.name.value() != orig.name.value()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700285 *out_msg << " (aka " << transformed.name.value() << ")";
Adam Lesinski28cacf02015-11-23 14:22:47 -0800286 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700287 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700288 *out_msg << orig.id.value();
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700289 }
Adam Lesinski28cacf02015-11-23 14:22:47 -0800290}
291
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800292bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
293 IAaptContext* context, SymbolTable* symbols,
294 xml::IPackageDeclStack* decls) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700295 CHECK(reference != nullptr);
Adam Lesinskibab4ef52017-06-01 15:22:57 -0700296 if (!reference->name && !reference->id) {
297 // This is @null.
298 return true;
299 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800300
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700301 Reference transformed_reference = *reference;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800302 TransformReferenceFromNamespace(decls, context->GetCompilationPackage(), &transformed_reference);
Adam Lesinski467f1712015-11-16 17:35:44 -0800303
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700304 std::string err_str;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800305 const SymbolTable::Symbol* s =
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800306 ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700307 if (s) {
308 // The ID may not exist. This is fine because of the possibility of building
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700309 // against libraries without assigned IDs.
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700310 // Ex: Linking against own resources when building a static library.
311 reference->id = s->id;
312 return true;
313 }
Adam Lesinski467f1712015-11-16 17:35:44 -0800314
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700315 DiagMessage error_msg(reference->GetSource());
316 error_msg << "resource ";
317 WriteResourceName(&error_msg, *reference, transformed_reference);
318 error_msg << " " << err_str;
319 context->GetDiagnostics()->Error(error_msg);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700320 return false;
Adam Lesinski467f1712015-11-16 17:35:44 -0800321}
322
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700323bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
324 EmptyDeclStack decl_stack;
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700325 bool error = false;
326 for (auto& package : table->packages) {
327 for (auto& type : package->types) {
328 for (auto& entry : type->entries) {
329 // Symbol state information may be lost if there is no value for the
330 // resource.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700331 if (entry->symbol_status.state != SymbolState::kUndefined &&
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700332 entry->values.empty()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700333 context->GetDiagnostics()->Error(
334 DiagMessage(entry->symbol_status.source)
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700335 << "no definition for declared symbol '"
336 << ResourceNameRef(package->name, type->type, entry->name)
337 << "'");
338 error = true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700339 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700340
Adam Lesinskif34b6f42017-03-03 16:33:26 -0800341 CallSite callsite = {ResourceNameRef(package->name, type->type, entry->name)};
342 ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
343 &table->string_pool, &decl_stack);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700344
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700345 for (auto& config_value : entry->values) {
346 config_value->value->Accept(&visitor);
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700347 }
348
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700349 if (visitor.HasError()) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700350 error = true;
351 }
352 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700353 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700354 }
355 return !error;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700356}
357
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700358} // namespace aapt