Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Index/IndexBody.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- IndexBody.cpp - Indexing statements --------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "IndexingContext.h"
10
#include "clang/AST/RecursiveASTVisitor.h"
11
#include "clang/AST/ASTLambda.h"
12
13
using namespace clang;
14
using namespace clang::index;
15
16
namespace {
17
18
class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> {
19
  IndexingContext &IndexCtx;
20
  const NamedDecl *Parent;
21
  const DeclContext *ParentDC;
22
  SmallVector<Stmt*, 16> StmtStack;
23
24
  typedef RecursiveASTVisitor<BodyIndexer> base;
25
26
51
  Stmt *getParentStmt() const {
27
51
    return StmtStack.size() < 2 ? 
nullptr0
: StmtStack.end()[-2];
28
51
  }
29
public:
30
  BodyIndexer(IndexingContext &indexCtx,
31
              const NamedDecl *Parent, const DeclContext *DC)
32
250
    : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { }
33
34
0
  bool shouldWalkTypesOfTypeLocs() const { return false; }
35
36
12.3k
  bool dataTraverseStmtPre(Stmt *S) {
37
12.3k
    StmtStack.push_back(S);
38
12.3k
    return true;
39
12.3k
  }
40
41
12.3k
  bool dataTraverseStmtPost(Stmt *S) {
42
12.3k
    assert(StmtStack.back() == S);
43
0
    StmtStack.pop_back();
44
12.3k
    return true;
45
12.3k
  }
46
47
146
  bool TraverseTypeLoc(TypeLoc TL) {
48
146
    IndexCtx.indexTypeLoc(TL, Parent, ParentDC);
49
146
    return true;
50
146
  }
51
52
4.05k
  bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
53
4.05k
    IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC);
54
4.05k
    return true;
55
4.05k
  }
56
57
  SymbolRoleSet getRolesForRef(const Expr *E,
58
3.98k
                               SmallVectorImpl<SymbolRelation> &Relations) {
59
3.98k
    SymbolRoleSet Roles{};
60
3.98k
    assert(!StmtStack.empty() && E == StmtStack.back());
61
3.98k
    if (StmtStack.size() == 1)
62
5
      return Roles;
63
3.97k
    auto It = StmtStack.end()-2;
64
7.85k
    while (isa<CastExpr>(*It) || 
isa<ParenExpr>(*It)3.96k
) {
65
3.89k
      if (auto ICE = dyn_cast<ImplicitCastExpr>(*It)) {
66
3.89k
        if (ICE->getCastKind() == CK_LValueToRValue)
67
2.11k
          Roles |= (unsigned)(unsigned)SymbolRole::Read;
68
3.89k
      }
69
3.89k
      if (It == StmtStack.begin())
70
19
        break;
71
3.88k
      --It;
72
3.88k
    }
73
3.97k
    const Stmt *Parent = *It;
74
75
3.97k
    if (auto BO = dyn_cast<BinaryOperator>(Parent)) {
76
2.03k
      if (BO->getOpcode() == BO_Assign && 
BO->getLHS()->IgnoreParenCasts() == E18
)
77
17
        Roles |= (unsigned)SymbolRole::Write;
78
79
2.03k
    } else 
if (auto 1.94k
UO1.94k
= dyn_cast<UnaryOperator>(Parent)) {
80
11
      if (UO->isIncrementDecrementOp()) {
81
6
        Roles |= (unsigned)SymbolRole::Read;
82
6
        Roles |= (unsigned)SymbolRole::Write;
83
6
      } else 
if (5
UO->getOpcode() == UO_AddrOf5
) {
84
3
        Roles |= (unsigned)SymbolRole::AddressOf;
85
3
      }
86
87
1.93k
    } else if (auto CA = dyn_cast<CompoundAssignOperator>(Parent)) {
88
0
      if (CA->getLHS()->IgnoreParenCasts() == E) {
89
0
        Roles |= (unsigned)SymbolRole::Read;
90
0
        Roles |= (unsigned)SymbolRole::Write;
91
0
      }
92
93
1.93k
    } else if (auto CE = dyn_cast<CallExpr>(Parent)) {
94
1.78k
      if (CE->getCallee()->IgnoreParenCasts() == E) {
95
1.77k
        addCallRole(Roles, Relations);
96
1.77k
        if (auto *ME = dyn_cast<MemberExpr>(E)) {
97
5
          if (auto *CXXMD = dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl()))
98
5
            if (CXXMD->isVirtual() && 
!ME->hasQualifier()0
) {
99
0
              Roles |= (unsigned)SymbolRole::Dynamic;
100
0
              auto BaseTy = ME->getBase()->IgnoreImpCasts()->getType();
101
0
              if (!BaseTy.isNull())
102
0
                if (auto *CXXRD = BaseTy->getPointeeCXXRecordDecl())
103
0
                  Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy,
104
0
                                         CXXRD);
105
0
            }
106
5
        }
107
1.77k
      } else 
if (auto 7
CXXOp7
= dyn_cast<CXXOperatorCallExpr>(CE)) {
108
6
        if (CXXOp->getNumArgs() > 0 && CXXOp->getArg(0)->IgnoreParenCasts() == E) {
109
6
          OverloadedOperatorKind Op = CXXOp->getOperator();
110
6
          if (Op == OO_Equal) {
111
1
            Roles |= (unsigned)SymbolRole::Write;
112
5
          } else if ((Op >= OO_PlusEqual && 
Op <= OO_PipeEqual4
) ||
113
5
                     Op == OO_LessLessEqual || Op == OO_GreaterGreaterEqual ||
114
5
                     Op == OO_PlusPlus || 
Op == OO_MinusMinus4
) {
115
1
            Roles |= (unsigned)SymbolRole::Read;
116
1
            Roles |= (unsigned)SymbolRole::Write;
117
4
          } else if (Op == OO_Amp) {
118
0
            Roles |= (unsigned)SymbolRole::AddressOf;
119
0
          }
120
6
        }
121
6
      }
122
1.78k
    }
123
124
3.97k
    return Roles;
125
3.98k
  }
126
127
  void addCallRole(SymbolRoleSet &Roles,
128
1.85k
                   SmallVectorImpl<SymbolRelation> &Relations) {
129
1.85k
    Roles |= (unsigned)SymbolRole::Call;
130
1.85k
    if (auto *FD = dyn_cast<FunctionDecl>(ParentDC))
131
1.83k
      Relations.emplace_back((unsigned)SymbolRole::RelationCalledBy, FD);
132
26
    else if (auto *MD = dyn_cast<ObjCMethodDecl>(ParentDC))
133
16
      Relations.emplace_back((unsigned)SymbolRole::RelationCalledBy, MD);
134
1.85k
  }
135
136
3.93k
  bool VisitDeclRefExpr(DeclRefExpr *E) {
137
3.93k
    SmallVector<SymbolRelation, 4> Relations;
138
3.93k
    SymbolRoleSet Roles = getRolesForRef(E, Relations);
139
3.93k
    return IndexCtx.handleReference(E->getDecl(), E->getLocation(),
140
3.93k
                                    Parent, ParentDC, Roles, Relations, E);
141
3.93k
  }
142
143
12
  bool VisitMemberExpr(MemberExpr *E) {
144
12
    SourceLocation Loc = E->getMemberLoc();
145
12
    if (Loc.isInvalid())
146
0
      Loc = E->getBeginLoc();
147
12
    SmallVector<SymbolRelation, 4> Relations;
148
12
    SymbolRoleSet Roles = getRolesForRef(E, Relations);
149
12
    return IndexCtx.handleReference(E->getMemberDecl(), Loc,
150
12
                                    Parent, ParentDC, Roles, Relations, E);
151
12
  }
152
153
  bool indexDependentReference(
154
      const Expr *E, const Type *T, const DeclarationNameInfo &NameInfo,
155
27
      llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
156
27
    if (!T)
157
0
      return true;
158
27
    const TemplateSpecializationType *TST =
159
27
        T->getAs<TemplateSpecializationType>();
160
27
    if (!TST)
161
0
      return true;
162
27
    TemplateName TN = TST->getTemplateName();
163
27
    const ClassTemplateDecl *TD =
164
27
        dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
165
27
    if (!TD)
166
0
      return true;
167
27
    CXXRecordDecl *RD = TD->getTemplatedDecl();
168
27
    if (!RD->hasDefinition())
169
1
      return true;
170
26
    RD = RD->getDefinition();
171
26
    std::vector<const NamedDecl *> Symbols =
172
26
        RD->lookupDependentName(NameInfo.getName(), Filter);
173
    // FIXME: Improve overload handling.
174
26
    if (Symbols.size() != 1)
175
13
      return true;
176
13
    SourceLocation Loc = NameInfo.getLoc();
177
13
    if (Loc.isInvalid())
178
0
      Loc = E->getBeginLoc();
179
13
    SmallVector<SymbolRelation, 4> Relations;
180
13
    SymbolRoleSet Roles = getRolesForRef(E, Relations);
181
13
    return IndexCtx.handleReference(Symbols[0], Loc, Parent, ParentDC, Roles,
182
13
                                    Relations, E);
183
26
  }
184
185
17
  bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
186
17
    const DeclarationNameInfo &Info = E->getMemberNameInfo();
187
17
    return indexDependentReference(
188
17
        E, E->getBaseType().getTypePtrOrNull(), Info,
189
17
        [](const NamedDecl *D) 
{ return D->isCXXInstanceMember(); }16
);
190
17
  }
191
192
10
  bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
193
10
    const DeclarationNameInfo &Info = E->getNameInfo();
194
10
    const NestedNameSpecifier *NNS = E->getQualifier();
195
10
    return indexDependentReference(
196
10
        E, NNS->getAsType(), Info,
197
10
        [](const NamedDecl *D) 
{ return !D->isCXXInstanceMember(); }9
);
198
10
  }
199
200
2
  bool VisitDesignatedInitExpr(DesignatedInitExpr *E) {
201
2
    for (DesignatedInitExpr::Designator &D : llvm::reverse(E->designators())) {
202
2
      if (D.isFieldDesignator() && D.getField())
203
0
        return IndexCtx.handleReference(D.getField(), D.getFieldLoc(), Parent,
204
0
                                        ParentDC, SymbolRoleSet(), {}, E);
205
2
    }
206
2
    return true;
207
2
  }
208
209
9
  bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) {
210
9
    SmallVector<SymbolRelation, 4> Relations;
211
9
    SymbolRoleSet Roles = getRolesForRef(E, Relations);
212
9
    return IndexCtx.handleReference(E->getDecl(), E->getLocation(),
213
9
                                    Parent, ParentDC, Roles, Relations, E);
214
9
  }
215
216
51
  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
217
51
    auto isDynamic = [](const ObjCMessageExpr *MsgE)->bool {
218
51
      if (MsgE->getReceiverKind() != ObjCMessageExpr::Instance)
219
15
        return false;
220
36
      if (auto *RecE = dyn_cast<ObjCMessageExpr>(
221
36
              MsgE->getInstanceReceiver()->IgnoreParenCasts())) {
222
0
        if (RecE->getMethodFamily() == OMF_alloc)
223
0
          return false;
224
0
      }
225
36
      return true;
226
36
    };
227
228
51
    if (ObjCMethodDecl *MD = E->getMethodDecl()) {
229
51
      SymbolRoleSet Roles{};
230
51
      SmallVector<SymbolRelation, 2> Relations;
231
51
      addCallRole(Roles, Relations);
232
51
      Stmt *Containing = getParentStmt();
233
234
51
      auto IsImplicitProperty = [](const PseudoObjectExpr *POE) -> bool {
235
26
        const auto *E = POE->getSyntacticForm();
236
26
        if (const auto *BinOp = dyn_cast<BinaryOperator>(E))
237
12
          E = BinOp->getLHS();
238
26
        const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E);
239
26
        if (!PRE)
240
4
          return false;
241
22
        if (PRE->isExplicitProperty())
242
6
          return false;
243
16
        if (const ObjCMethodDecl *Getter = PRE->getImplicitPropertyGetter()) {
244
          // Class properties that are explicitly defined using @property
245
          // declarations are represented implicitly as there is no ivar for
246
          // class properties.
247
16
          if (Getter->isClassMethod() &&
248
16
              
Getter->getCanonicalDecl()->findPropertyDecl()12
)
249
4
            return false;
250
16
        }
251
12
        return true;
252
16
      };
253
51
      bool IsPropCall = Containing && isa<PseudoObjectExpr>(Containing);
254
      // Implicit property message sends are not 'implicit'.
255
51
      if ((E->isImplicit() || 
IsPropCall25
) &&
256
51
          
!(26
IsPropCall26
&&
257
26
            IsImplicitProperty(cast<PseudoObjectExpr>(Containing))))
258
14
        Roles |= (unsigned)SymbolRole::Implicit;
259
260
51
      if (isDynamic(E)) {
261
36
        Roles |= (unsigned)SymbolRole::Dynamic;
262
263
36
        auto addReceivers = [&](const ObjCObjectType *Ty) {
264
36
          if (!Ty)
265
0
            return;
266
36
          if (const auto *clsD = Ty->getInterface()) {
267
33
            Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy,
268
33
                                   clsD);
269
33
          }
270
36
          for (const auto *protD : Ty->quals()) {
271
9
            Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy,
272
9
                                   protD);
273
9
          }
274
36
        };
275
36
        QualType recT = E->getReceiverType();
276
36
        if (const auto *Ptr = recT->getAs<ObjCObjectPointerType>())
277
36
          addReceivers(Ptr->getObjectType());
278
0
        else
279
0
          addReceivers(recT->getAs<ObjCObjectType>());
280
36
      }
281
282
51
      return IndexCtx.handleReference(MD, E->getSelectorStartLoc(),
283
51
                                      Parent, ParentDC, Roles, Relations, E);
284
51
    }
285
0
    return true;
286
51
  }
287
288
22
  bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
289
22
    if (E->isExplicitProperty()) {
290
6
      SmallVector<SymbolRelation, 2> Relations;
291
6
      SymbolRoleSet Roles = getRolesForRef(E, Relations);
292
6
      return IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(),
293
6
                                      Parent, ParentDC, Roles, Relations, E);
294
16
    } else if (const ObjCMethodDecl *Getter = E->getImplicitPropertyGetter()) {
295
      // Class properties that are explicitly defined using @property
296
      // declarations are represented implicitly as there is no ivar for class
297
      // properties.
298
16
      if (Getter->isClassMethod()) {
299
12
        if (const auto *PD = Getter->getCanonicalDecl()->findPropertyDecl()) {
300
4
          SmallVector<SymbolRelation, 2> Relations;
301
4
          SymbolRoleSet Roles = getRolesForRef(E, Relations);
302
4
          return IndexCtx.handleReference(PD, E->getLocation(), Parent,
303
4
                                          ParentDC, Roles, Relations, E);
304
4
        }
305
12
      }
306
16
    }
307
308
    // No need to do a handleReference for the objc method, because there will
309
    // be a message expr as part of PseudoObjectExpr.
310
12
    return true;
311
22
  }
312
313
2
  bool VisitMSPropertyRefExpr(MSPropertyRefExpr *E) {
314
2
    return IndexCtx.handleReference(E->getPropertyDecl(), E->getMemberLoc(),
315
2
                                    Parent, ParentDC, SymbolRoleSet(), {}, E);
316
2
  }
317
318
1
  bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) {
319
1
    return IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(),
320
1
                                    Parent, ParentDC, SymbolRoleSet(), {}, E);
321
1
  }
322
323
4
  bool passObjCLiteralMethodCall(const ObjCMethodDecl *MD, const Expr *E) {
324
4
    SymbolRoleSet Roles{};
325
4
    SmallVector<SymbolRelation, 2> Relations;
326
4
    addCallRole(Roles, Relations);
327
4
    Roles |= (unsigned)SymbolRole::Implicit;
328
4
    return IndexCtx.handleReference(MD, E->getBeginLoc(), Parent, ParentDC,
329
4
                                    Roles, Relations, E);
330
4
  }
331
332
2
  bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) {
333
2
    if (ObjCMethodDecl *MD = E->getBoxingMethod()) {
334
2
      return passObjCLiteralMethodCall(MD, E);
335
2
    }
336
0
    return true;
337
2
  }
338
339
1
  bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) {
340
1
    if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) {
341
1
      return passObjCLiteralMethodCall(MD, E);
342
1
    }
343
0
    return true;
344
1
  }
345
346
1
  bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) {
347
1
    if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) {
348
1
      return passObjCLiteralMethodCall(MD, E);
349
1
    }
350
0
    return true;
351
1
  }
352
353
30
  bool VisitCXXConstructExpr(CXXConstructExpr *E) {
354
30
    SymbolRoleSet Roles{};
355
30
    SmallVector<SymbolRelation, 2> Relations;
356
30
    addCallRole(Roles, Relations);
357
30
    return IndexCtx.handleReference(E->getConstructor(), E->getLocation(),
358
30
                                    Parent, ParentDC, Roles, Relations, E);
359
30
  }
360
361
  bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E,
362
1.74k
                                   DataRecursionQueue *Q = nullptr) {
363
1.74k
    if (E->getOperatorLoc().isInvalid())
364
0
      return true; // implicit.
365
1.74k
    return base::TraverseCXXOperatorCallExpr(E, Q);
366
1.74k
  }
367
368
64
  bool VisitDeclStmt(DeclStmt *S) {
369
64
    if (IndexCtx.shouldIndexFunctionLocalSymbols()) {
370
17
      IndexCtx.indexDeclGroupRef(S->getDeclGroup());
371
17
      return true;
372
17
    }
373
374
47
    DeclGroupRef DG = S->getDeclGroup();
375
94
    for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; 
++I47
) {
376
47
      const Decl *D = *I;
377
47
      if (!D)
378
0
        continue;
379
47
      if (!isFunctionLocalSymbol(D))
380
4
        IndexCtx.indexTopLevelDecl(D);
381
47
    }
382
383
47
    return true;
384
64
  }
385
386
  bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
387
8
                             Expr *Init) {
388
8
    if (C->capturesThis() || C->capturesVLAType())
389
0
      return true;
390
391
8
    if (!base::TraverseStmt(Init))
392
0
      return false;
393
394
8
    if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols())
395
8
      return IndexCtx.handleReference(C->getCapturedVar(), C->getLocation(),
396
8
                                      Parent, ParentDC, SymbolRoleSet());
397
398
0
    return true;
399
8
  }
400
401
  // RecursiveASTVisitor visits both syntactic and semantic forms, duplicating
402
  // the things that we visit. Make sure to only visit the semantic form.
403
  // Also visit things that are in the syntactic form but not the semantic one,
404
  // for example the indices in DesignatedInitExprs.
405
15
  bool TraverseInitListExpr(InitListExpr *S, DataRecursionQueue *Q = nullptr) {
406
15
    auto visitForm = [&](InitListExpr *Form) {
407
15
      for (Stmt *SubStmt : Form->children()) {
408
15
        if (!TraverseStmt(SubStmt, Q))
409
0
          return false;
410
15
      }
411
15
      return true;
412
15
    };
413
414
15
    auto visitSyntacticDesignatedInitExpr = [&](DesignatedInitExpr *E) -> bool {
415
10
      for (DesignatedInitExpr::Designator &D : llvm::reverse(E->designators())) {
416
10
        if (D.isFieldDesignator() && 
D.getField()4
)
417
3
          return IndexCtx.handleReference(D.getField(), D.getFieldLoc(),
418
3
                                          Parent, ParentDC, SymbolRoleSet(),
419
3
                                          {}, E);
420
10
      }
421
6
      return true;
422
9
    };
423
424
15
    InitListExpr *SemaForm = S->isSemanticForm() ? 
S14
:
S->getSemanticForm()1
;
425
15
    InitListExpr *SyntaxForm = S->isSemanticForm() ? 
S->getSyntacticForm()14
:
S1
;
426
427
15
    if (SemaForm) {
428
      // Visit things present in syntactic form but not the semantic form.
429
15
      if (SyntaxForm) {
430
13
        for (Expr *init : SyntaxForm->inits()) {
431
13
          if (auto *DIE = dyn_cast<DesignatedInitExpr>(init))
432
9
            visitSyntacticDesignatedInitExpr(DIE);
433
13
        }
434
12
      }
435
15
      return visitForm(SemaForm);
436
15
    }
437
438
    // No semantic, try the syntactic.
439
0
    if (SyntaxForm) {
440
0
      return visitForm(SyntaxForm);
441
0
    }
442
443
0
    return true;
444
0
  }
445
446
6
  bool VisitOffsetOfExpr(OffsetOfExpr *S) {
447
20
    for (unsigned I = 0, E = S->getNumComponents(); I != E; 
++I14
) {
448
14
      const OffsetOfNode &Component = S->getComponent(I);
449
14
      if (Component.getKind() == OffsetOfNode::Field)
450
8
        IndexCtx.handleReference(Component.getField(), Component.getEndLoc(),
451
8
                                 Parent, ParentDC, SymbolRoleSet(), {});
452
      // FIXME: Try to resolve dependent field references.
453
14
    }
454
6
    return true;
455
6
  }
456
457
6
  bool VisitParmVarDecl(ParmVarDecl* D) {
458
    // Index the parameters of lambda expression.
459
6
    if (IndexCtx.shouldIndexFunctionLocalSymbols()) {
460
6
      const auto *DC = D->getDeclContext();
461
6
      if (DC && isLambdaCallOperator(DC))
462
6
        IndexCtx.handleDecl(D);
463
6
    }
464
6
    return true;
465
6
  }
466
467
1
  bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E) {
468
1
    SmallVector<SymbolRelation, 4> Relations;
469
1
    SymbolRoleSet Roles = getRolesForRef(E, Relations);
470
1
    for (auto *D : E->decls())
471
1
      IndexCtx.handleReference(D, E->getNameLoc(), Parent, ParentDC, Roles,
472
1
                               Relations, E);
473
1
    return true;
474
1
  }
475
};
476
477
} // anonymous namespace
478
479
void IndexingContext::indexBody(const Stmt *S, const NamedDecl *Parent,
480
331
                                const DeclContext *DC) {
481
331
  if (!S)
482
81
    return;
483
484
250
  if (!DC)
485
66
    DC = Parent->getLexicalDeclContext();
486
250
  BodyIndexer(*this, Parent, DC).TraverseStmt(const_cast<Stmt*>(S));
487
250
}