Skip to content

Commit

Permalink
feat: show operand of noexcept specifier
Browse files Browse the repository at this point in the history
  • Loading branch information
sdkrystian committed Dec 1, 2023
1 parent 253cb4d commit a1379c8
Show file tree
Hide file tree
Showing 19 changed files with 326 additions and 91 deletions.
2 changes: 2 additions & 0 deletions include/mrdocs/Metadata/Function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ struct FunctionInfo
FnFlags0 specs0{.raw{0}};
FnFlags1 specs1{.raw{0}};

NoexceptInfo Noexcept;

//--------------------------------------------

explicit FunctionInfo(SymbolID ID) noexcept
Expand Down
65 changes: 41 additions & 24 deletions include/mrdocs/Metadata/Specifiers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <mrdocs/Platform.hpp>
#include <mrdocs/Dom.hpp>
#include <string>
#include <string_view>

namespace clang {
Expand Down Expand Up @@ -70,30 +71,31 @@ enum class ExplicitKind
*/
enum class NoexceptKind
{
None = 0,
// throw()
ThrowNone,
// throw(type-id-list)
Throw,
// throw(...) (microsoft extension)
ThrowAny,
// __declspec(nothrow) (microsoft extension)
NoThrow,
// noexcept-specifier, no constant-expression
Noexcept,
// noexcept-specifier, constant-expression evaluates to false
NoexceptFalse,
// noexcept-specifier, constant-expression evaluates to true
NoexceptTrue,
// noexcept-specifier, dependent constant-expression
NoexceptDependent,

// not evaluated yet, for special member function
Unevaluated,
// not instantiated yet
Uninstantiated,
// not parsed yet
Unparsed
/** Potentially-throwing exception specification
*/
False = 0,
/** Non-throwing exception specification
*/
True,
/** Dependent exception specification
*/
Dependent
};

// KRYSTIAN FIXME: this needs to be improved (a lot)
struct NoexceptInfo
{
/** Whether a noexcept-specifier was user-written.
*/
bool Implicit = true;

/** The evaluated exception specification.
*/
NoexceptKind Kind = NoexceptKind::False;

/** The operand of the noexcept-specifier, if any.
*/
std::string Operand;
};

/** Operator kinds
Expand Down Expand Up @@ -183,6 +185,21 @@ MRDOCS_DECL dom::String toString(NoexceptKind kind) noexcept;
MRDOCS_DECL dom::String toString(ReferenceKind kind) noexcept;
MRDOCS_DECL dom::String toString(StorageClassKind kind) noexcept;

/** Convert NoexceptInfo to a string.
@param resolved If true, the operand is not shown when
the exception specification is non-dependent.
@param implicit If true, implicit exception specifications
are shown.
*/
MRDOCS_DECL
dom::String
toString(
NoexceptInfo info,
bool resolved = false,
bool implicit = false);

} // mrdocs
} // clang

Expand Down
2 changes: 1 addition & 1 deletion include/mrdocs/Metadata/Type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ struct FunctionTypeInfo
std::vector<std::unique_ptr<TypeInfo>> ParamTypes;
QualifierKind CVQualifiers = QualifierKind::None;
ReferenceKind RefQualifier = ReferenceKind::None;
NoexceptKind ExceptionSpec = NoexceptKind::None;
NoexceptInfo ExceptionSpec;

TypeInfo* innerType() const noexcept override
{
Expand Down
1 change: 1 addition & 0 deletions mrdocs.rnc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ grammar
Access?,
ID,
attribute class { "constructor"|"destructor"|"conversion" } ?,
attribute exception-spec { text } ?,
Location *,
(
Attr * |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
{{~#if symbol.isConst}} const{{/if~}}
{{#if symbol.isVolatile}} volatile{{/if~}}
{{#if symbol.refQualifier}} {{symbol.refQualifier}}{{/if~}}
{{#if (eq symbol.exceptionSpec "noexcept")}} noexcept{{/if~}}
{{#if symbol.exceptionSpec}} {{symbol.exceptionSpec}}{{/if~}}
{{#if (eq symbol.class "normal")}}{{>declarator-after symbol.return}}{{/if~}}
{{#if symbol.hasOverrideAttr}} override{{/if~}}
{{#if symbol.isFinal}} final{{/if~}}
Expand Down
135 changes: 125 additions & 10 deletions src/lib/AST/ASTVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,9 +709,15 @@ class ASTVisitor

//------------------------------------------------

std::string
getTypeAsString(
QualType T)
std::string getExprAsString(const Expr* E)
{
std::string result;
llvm::raw_string_ostream stream(result);
E->printPretty(stream, nullptr, context_.getPrintingPolicy());
return result;
}

std::string getTypeAsString(QualType T)
{
return T.getAsString(context_.getPrintingPolicy());
}
Expand Down Expand Up @@ -970,8 +976,7 @@ class ASTVisitor
T->getRefQualifier());
I->CVQualifiers = convertToQualifierKind(
T->getMethodQuals().getFastQualifiers());
I->ExceptionSpec = convertToNoexceptKind(
T->getExceptionSpecType());
buildNoexceptInfo(I->ExceptionSpec, T);
*std::exchange(inner, &I->ReturnType) = std::move(I);
qt = T->getReturnType();
continue;
Expand Down Expand Up @@ -1270,6 +1275,99 @@ class ASTVisitor
V.getZExtValue());
}

void
buildNoexceptInfo(
NoexceptInfo& I,
ExceptionSpecificationType Kind,
const Expr* E)
{
I.Implicit = Kind == ExceptionSpecificationType::EST_None;
I.Kind = convertToNoexceptKind(Kind);

if(! E)
return;

I.Operand = getExprAsString(E);
}

void
buildNoexceptInfo(
NoexceptInfo& I,
TypeSourceInfo* FTSI)
{
MRDOCS_ASSERT(FTSI && FTSI->getType()->isFunctionProtoType());
if(auto* FPT = FTSI->getType()->getAs<FunctionProtoType>())
{
buildNoexceptInfo(I,
FPT->getExceptionSpecType(),
FPT->getNoexceptExpr());
}
}

void
buildNoexceptInfo(
NoexceptInfo& I,
const FunctionProtoType* FPT)
{
MRDOCS_ASSERT(FPT);
I.Implicit = ! FPT->hasNoexceptExceptionSpec();
#if 0
// if the exception specification is unevaluated,
// we just consider it to be dependent
if(FPT->getExceptionSpecType() ==
ExceptionSpecificationType::EST_Unevaluated)
I.Kind = NoexceptKind::Dependent;
else
I.Kind = convertToNoexceptKind(FPT->canThrow());
#endif
I.Kind = convertToNoexceptKind(FPT->getExceptionSpecType());

// store the operand, if any
if(Expr* NoexceptExpr = FPT->getNoexceptExpr())
I.Operand = getExprAsString(NoexceptExpr);
}

#if 0
void
buildNoexceptInfo(
NoexceptInfo& I,
ExceptionSpecificationType Kind,
const Expr* E)
{
if(Kind == ExceptionSpecificationType::EST_None)
return;
I.Kind = convertToNoexceptKind(Kind);

if(! E)
return;
#if 0
I.Operand = getSourceCode(
E->getSourceRange());
#else

llvm::raw_string_ostream stream(I.Operand);
E->printPretty(stream, nullptr, context_.getPrintingPolicy());
#endif
}

void
buildNoexceptInfo(
NoexceptInfo& I,
FunctionDecl* FD)
{
auto Kind = FD->getExceptionSpecType();
if(Kind == ExceptionSpecificationType::EST_None)
return;
const Expr* E = nullptr;
if(auto FTL = FD->getFunctionTypeLoc())
{
if(auto* FPT = FTL.getAs<FunctionProtoTypeLoc>().getTypePtr())
E = FPT->getNoexceptExpr();
}
buildNoexceptInfo(I, Kind, E);
}
#endif

void
buildExprInfo(
ExprInfo& I,
Expand Down Expand Up @@ -1308,6 +1406,16 @@ class ASTVisitor
I.Value.emplace(getValue<T>(V));
}

QualType
getDeclaratorType(
const DeclaratorDecl* DD)
{
if(auto* TSI = DD->getTypeSourceInfo();
TSI && ! TSI->getType().isNull())
return TSI->getType();
return DD->getType();
}

std::unique_ptr<TParam>
buildTemplateParam(
const NamedDecl* N)
Expand Down Expand Up @@ -2151,6 +2259,17 @@ class ASTVisitor
addSourceLocation(I, D->getBeginLoc(),
D->isThisDeclarationADefinition(), documented);

// KRYSTIAN TODO: move other extraction that requires
// a valid function type here
if(auto FT = getDeclaratorType(D); ! FT.isNull())
{
const auto* FPT = FT->template getAs<FunctionProtoType>();

buildNoexceptInfo(I.Noexcept, FPT);

I.specs0.hasTrailingReturn |= FPT->hasTrailingReturn();
}

//
// FunctionDecl
//
Expand All @@ -2165,14 +2284,10 @@ class ASTVisitor
// subsumes D->hasAttr<C11NoReturnAttr>()
// subsumes D->getType()->getAs<FunctionType>()->getNoReturnAttr()
I.specs0.hasOverrideAttr |= D->template hasAttr<OverrideAttr>();
if(auto const* FP = D->getType()->template getAs<FunctionProtoType>())
I.specs0.hasTrailingReturn |= FP->hasTrailingReturn();
I.specs0.constexprKind |=
convertToConstexprKind(
D->getConstexprKind());
I.specs0.exceptionSpec |=
convertToNoexceptKind(
D->getExceptionSpecType());

I.specs0.overloadedOperator |=
convertToOperatorKind(
D->getOverloadedOperator());
Expand Down
47 changes: 35 additions & 12 deletions src/lib/AST/ASTVisitorHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,20 +114,43 @@ convertToNoexceptKind(
{
using OldKind = ExceptionSpecificationType;
using NewKind = NoexceptKind;
// KRYSTIAN TODO: right now we convert pre-C++17 dynamic exception
// specifications to an (roughly) equivalent noexcept-specifier
switch(spec)
{
case OldKind::EST_None: return NewKind::None;
case OldKind::EST_DynamicNone: return NewKind::ThrowNone;
case OldKind::EST_Dynamic: return NewKind::Throw;
case OldKind::EST_MSAny: return NewKind::ThrowAny;
case OldKind::EST_NoThrow: return NewKind::NoThrow;
case OldKind::EST_BasicNoexcept: return NewKind::Noexcept;
case OldKind::EST_DependentNoexcept: return NewKind::NoexceptDependent;
case OldKind::EST_NoexceptFalse: return NewKind::NoexceptFalse;
case OldKind::EST_NoexceptTrue: return NewKind::NoexceptTrue;
case OldKind::EST_Unevaluated: return NewKind::Unevaluated;
case OldKind::EST_Uninstantiated: return NewKind::Uninstantiated;
case OldKind::EST_Unparsed: return NewKind::Unparsed;
case OldKind::EST_None:
case OldKind::EST_MSAny:
case OldKind::EST_Unevaluated:
case OldKind::EST_Uninstantiated:
// we *shouldn't* ever encounter an unparsed exception specification,
// assuming that clang is working correctly...
case OldKind::EST_Unparsed:
case OldKind::EST_Dynamic:
case OldKind::EST_NoexceptFalse:
return NewKind::False;
case OldKind::EST_NoThrow:
case OldKind::EST_BasicNoexcept:
case OldKind::EST_NoexceptTrue:
case OldKind::EST_DynamicNone:
return NewKind::True;
case OldKind::EST_DependentNoexcept:
return NewKind::Dependent;
default:
MRDOCS_UNREACHABLE();
}
}

NoexceptKind
convertToNoexceptKind(

Check warning on line 144 in src/lib/AST/ASTVisitorHelpers.hpp

View workflow job for this annotation

GitHub Actions / Clang 16.0: C++20

clang++-16: unused function 'convertToNoexceptKind' ([-Wunused-function])

Check warning on line 144 in src/lib/AST/ASTVisitorHelpers.hpp

View workflow job for this annotation

GitHub Actions / GCC 13.1: C++20

g++-13: ‘clang::mrdocs::NoexceptKind clang::mrdocs::{anonymous}::convertToNoexceptKind(clang::CanThrowResult)’ defined but not used ([-Wunused-function])
CanThrowResult kind)
{
using OldKind = CanThrowResult;
using NewKind = NoexceptKind;
switch(kind)
{
case OldKind::CT_Can: return NewKind::False;
case OldKind::CT_Cannot: return NewKind::True;
case OldKind::CT_Dependent: return NewKind::Dependent;
default:
MRDOCS_UNREACHABLE();
}
Expand Down
Loading

0 comments on commit a1379c8

Please sign in to comment.