[PATCH] Sema: Eliminate recursion from checkForFunctionCall()

Duncan P. N. Exon Smith dexonsmith at apple.com
Thu Jul 23 09:56:01 PDT 2015


> On 2015-Jul-23, at 08:57, Vedant Kumar <vsk at apple.com> wrote:
> 
> Ok! The NFC preparatory patch is in part1, and the behavior-changing work is in part2.
> 
> No problems with check-clang.
> 
> <checkForFunctionCall-part1.patch><checkForFunctionCall-part2.patch>

Almost there.  A bunch more comments on the first patch, but some can
be left for follow-ups.  If you fix the function name and flatten the
`MCE` block I'll commit for you.

> diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
> index f2ff48a..ff21076 100644
> --- a/lib/Sema/AnalysisBasedWarnings.cpp
> +++ b/lib/Sema/AnalysisBasedWarnings.cpp
> @@ -171,6 +171,48 @@ enum RecursiveState {
>    FoundPathWithNoRecursiveCall
>  };
>  
> +static RecursiveState nextRecursiveStateInPath(const FunctionDecl *FD,

LLVM coding style prescribes verbs for function names.

Maybe, `getNextRecursiveStateInPath()`?  `getNextRecursiveState()`?

> +                                               CFGBlock &Block,
> +                                               RecursiveState State) {
> +  if (State != FoundPathWithNoRecursiveCall)
> +    return State;

Can you just check this before calling the function?  If so, you don't
need to pass `State` in at all.

Actually, this whole function looks like it's just checking for a
recursive path.  You could change it to return `bool`, and rename it
something like `hasRecursiveCall()`, and leave the state-related logic
out of the helper.  Maybe better in a follow-up patch?

> +
> +  // If the current state is FoundPathWithNoRecursiveCall, the successors
> +  // will be either FoundPathWithNoRecursiveCall or FoundPath.  To determine
> +  // which, process all the Stmt's in this block to find any recursive calls.
> +  for (const auto &B : Block) {
> +    if (B.getKind() != CFGElement::Statement)
> +      continue;
> +
> +    const CallExpr *CE = dyn_cast<CallExpr>(B.getAs<CFGStmt>()->getStmt());
> +    if (!CE || !CE->getCalleeDecl() ||
> +        CE->getCalleeDecl()->getCanonicalDecl() != FD)
> +      continue;
> +
> +    // Skip function calls which are qualified with a templated class.
> +    if (const DeclRefExpr *DRE =
> +            dyn_cast<DeclRefExpr>(CE->getCallee()->IgnoreParenImpCasts())) {
> +      if (NestedNameSpecifier *NNS = DRE->getQualifier()) {
> +        if (NNS->getKind() == NestedNameSpecifier::TypeSpec &&
> +            isa<TemplateSpecializationType>(NNS->getAsType())) {
> +          continue;
> +        }
> +      }
> +    }
> +
> +    if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) {
> +      if (isa<CXXThisExpr>(MCE->getImplicitObjectArgument()) ||
> +          !MCE->getMethodDecl()->isVirtual()) {
> +        return FoundPath;
> +      }
> +    } else {
> +      return FoundPath;
> +    }

I think this would be clearer like this:

    const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE);
    if (!MCE || isa<CXXThisExpr>(MCE->getImplicitObjectArgument()) ||
        !MCE->getMethodDecl()->isVirtual())
      return FoundPath;

> +  }
>  
> +  return State;

This is always `FoundPathWithNoRecursiveCall`, right?

> +}
> +
>  static void checkForFunctionCall(Sema &S, const FunctionDecl *FD,
>                                   CFGBlock &Block, unsigned ExitID,
>                                   llvm::SmallVectorImpl<RecursiveState> &States,
> @@ -187,42 +229,7 @@ static void checkForFunctionCall(Sema &S, const FunctionDecl *FD,
>    if (ID == ExitID && State == FoundPathWithNoRecursiveCall)
>      return;
>  
> -  if (State == FoundPathWithNoRecursiveCall) {
> -    // If the current state is FoundPathWithNoRecursiveCall, the successors
> -    // will be either FoundPathWithNoRecursiveCall or FoundPath.  To determine
> -    // which, process all the Stmt's in this block to find any recursive calls.
> -    for (const auto &B : Block) {
> -      if (B.getKind() != CFGElement::Statement)
> -        continue;
> -
> -      const CallExpr *CE = dyn_cast<CallExpr>(B.getAs<CFGStmt>()->getStmt());
> -      if (CE && CE->getCalleeDecl() &&
> -          CE->getCalleeDecl()->getCanonicalDecl() == FD) {
> -
> -        // Skip function calls which are qualified with a templated class.
> -        if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(
> -                CE->getCallee()->IgnoreParenImpCasts())) {
> -          if (NestedNameSpecifier *NNS = DRE->getQualifier()) {
> -            if (NNS->getKind() == NestedNameSpecifier::TypeSpec &&
> -                isa<TemplateSpecializationType>(NNS->getAsType())) {
> -               continue;
> -            }
> -          }
> -        }
> -
> -        if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) {
> -          if (isa<CXXThisExpr>(MCE->getImplicitObjectArgument()) ||
> -              !MCE->getMethodDecl()->isVirtual()) {
> -            State = FoundPath;
> -            break;
> -          }
> -        } else {
> -          State = FoundPath;
> -          break;
> -        }
> -      }
> -    }
> -  }
> +  State = nextRecursiveStateInPath(FD, Block, State);

For clarity: what I'm suggesting above is to change this call to:

    if (State == FoundPathWithNoRecursiveCall)
      State = getNextRecursiveState(FD, Block);

or, even better:

    if (State == FoundPathWithNoRecursiveCall)
      if (hasRecursiveCallInPath(FD, Block))
        State = FoundPath;

In either case, this could be merged with the early exit:

    if (State == FoundPathWithNoRecursiveCall)
      if (ExitID == ID)
        return;
      if (hasRecusiveCallInPath(FD, Block)
        State = FoundPath;
    }

These might be better left to a follow-up though.

>  
>    for (CFGBlock::succ_iterator I = Block.succ_begin(), E = Block.succ_end();
>         I != E; ++I)
> 





More information about the cfe-commits mailing list