clang ast: how do I find out whether a catch is done by const reference

Hi,

I want to write a check that tests whether a catch is done by const reference or not. I currently have the following code but it can’t distinguish between const reference and reference:

  const auto *catchStmt = Result.Nodes.getNodeAs<CXXCatchStmt>("catch");
  if (catchStmt != nullptr) {
    auto caughtType = catchStmt->getCaughtType();
    auto *type = caughtType.getTypePtr();
    auto *varDecl = catchStmt->getExceptionDecl();
    if (type->isPointerType()) {
      diag(varDecl->getLocStart(),
           "catch by const reference instead of pointer");
    } else if (!type->isReferenceType()) {
      diag(varDecl->getLocStart(), "catch by const reference");
    } else {
      if (caughtType.isConstQualified() == false) {
        diag(varDecl->getLocStart(),
             "catch by const reference to avoid unnecessary copies");
      }
    }
  }

I tried looking at the varDecl but from there I can’t even find out how to get the QualType. Can somebody give me a hint?

Regards
Tobias

Hi,

I want to write a check that tests whether a catch is done by const reference or not. I currently have the following code but it can’t distinguish between const reference and reference:

  const auto *catchStmt = Result.Nodes.getNodeAs<CXXCatchStmt>("catch");
  if (catchStmt != nullptr) {
    auto caughtType = catchStmt->getCaughtType();

This returns the QualType you are looking for.

caughType.isConstant() && caughtType->isLValueReferenceType();

should tell you if it's a const-qualified lvalue reference type.

~Aaron

Hi,

I want to write a check that tests whether a catch is done by const reference or not. I currently have the following code but it can’t distinguish between const reference and reference:

const auto *catchStmt = Result.Nodes.getNodeAs(“catch”);
if (catchStmt != nullptr) {
auto caughtType = catchStmt->getCaughtType();

This returns the QualType you are looking for.

caughType.isConstant() && caughtType->isLValueReferenceType();

should tell you if it’s a const-qualified lvalue reference type.

~Aaron

This gives me the same result as using QualType::isConstQualified() (see below). Both my examples are detected as not being caught by const reference:

void catchByReference() {
try {
testThrowFunc();
} catch (logic_error &e) {
}
}

void catchByConstReference() {
try {
testThrowFunc();
} catch (const logic_error &e) {
}
}

giving this result:

/Users/tobias/Documents/Development/git/llvm/build/ninja-debug/…/…/llvm/tools/clang/tools/extra/test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:44:12: warning: catch by const reference to avoid unnecessary copies [misc-throw-by-value-catch-by-reference]
} catch (logic_error &e) {
^
/Users/tobias/Documents/Development/git/llvm/build/ninja-debug/…/…/llvm/tools/clang/tools/extra/test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:51:12: warning: catch by const reference to avoid unnecessary copies [misc-throw-by-value-catch-by-reference]
} catch (const logic_error &e) {

so for both caughtTypes of these catch statements, neither isConstant() nor isConstQualified() returns true.

Regards
Tobias

Hi,

I want to write a check that tests whether a catch is done by const
reference or not. I currently have the following code but it can’t
distinguish between const reference and reference:

const auto *catchStmt = Result.Nodes.getNodeAs<CXXCatchStmt>("catch");
if (catchStmt != nullptr) {
   auto caughtType = catchStmt->getCaughtType();

This returns the QualType you are looking for.

caughType.isConstant() && caughtType->isLValueReferenceType();

should tell you if it's a const-qualified lvalue reference type.

~Aaron

This gives me the same result as using QualType::isConstQualified() (see
below). Both my examples are detected as not being caught by const
reference:

void catchByReference() {
  try {
    testThrowFunc();
  } catch (logic_error &e) {
  }
}

void catchByConstReference() {
  try {
    testThrowFunc();
  } catch (const logic_error &e) {
  }
}

giving this result:

/Users/tobias/Documents/Development/git/llvm/build/ninja-debug/../../llvm/tools/clang/tools/extra/test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:44:12:
warning: catch by const reference to avoid unnecessary copies
[misc-throw-by-value-catch-by-reference]
  } catch (logic_error &e) {
           ^
/Users/tobias/Documents/Development/git/llvm/build/ninja-debug/../../llvm/tools/clang/tools/extra/test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp:51:12:
warning: catch by const reference to avoid unnecessary copies
[misc-throw-by-value-catch-by-reference]
  } catch (const logic_error &e) {

so for both caughtTypes of these catch statements, neither isConstant() nor
isConstQualified() returns true.

Do you get the same results if you look at the canonical type instead?

auto QT = caughtType->getCaughtType().getCanonicalType();
if (QT.isConstQualified() && QT->isLValueReferenceType()) ...

~Aaron

yes, the result is the same. If someone wants a compilable example: https://github.com/iso8859-1/clang-tools-extra/tree/tobias-additional-checks (a reasonably new fork of the extra tools). The branch “tobias-additional-checks” contains the code.

Thank you
Tobias

Sorry, I got distracted by some clang-query crashes when trying to
make a matcher for this. :wink: The issue is that the top level is not
const qualified. You need to look at what the lvalue reference is
referencing to see if that is const qualified.

clang-query> match
catchStmt(has(varDecl(hasType(references(isConstQualified())))))

Match #1:

E:\Aaron Ballman\Documents\test.cpp:15:5: note: "root" binds here
  } catch (const logic_error &e) {
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 match.

~Aaron