sammccall updated this revision to Diff 300106. sammccall marked 2 inline comments as done. sammccall added a comment.
Address review comments. Add hasAttrName() and make isImplicit() support Attr too. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D89743/new/ https://reviews.llvm.org/D89743 Files: clang/docs/LibASTMatchersReference.html clang/include/clang/AST/ASTFwd.h clang/include/clang/AST/ASTTypeTraits.h clang/include/clang/ASTMatchers/ASTMatchFinder.h clang/include/clang/ASTMatchers/ASTMatchers.h clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/lib/AST/ASTTypeTraits.cpp clang/lib/ASTMatchers/ASTMatchFinder.cpp clang/lib/ASTMatchers/ASTMatchersInternal.cpp clang/lib/ASTMatchers/Dynamic/Registry.cpp clang/unittests/AST/ASTTypeTraitsTest.cpp clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
Index: clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -1875,6 +1875,26 @@ nestedNameSpecifier())); } +TEST_P(ASTMatchersTest, Attr) { + if (GetParam().isCXX11OrLater()) { + EXPECT_TRUE(matches("struct [[clang::warn_unused_result]] F{};", + attr(hasAttrName("warn_unused_result")))); + EXPECT_TRUE(notMatches("struct [[clang::warn_unused_result]] F{};", + attr(hasAttrName("clang::warn_unused_result")))); + } + if (GetParam().isCXX17OrLater()) { + EXPECT_TRUE( + matches("struct [[nodiscard]] F{};", attr(hasAttrName("nodiscard")))); + EXPECT_TRUE(notMatches("struct [[nodiscard]] F{};", + attr(hasAttrName("warn_unused_result")))); + } + EXPECT_TRUE(matches("int x(int * __attribute__((nonnull)) );", + attr(hasAttrName("nonnull")))); + // On windows, some nodes an implicit visibility attribute. + EXPECT_TRUE( + notMatches("struct F{}; int x(int *);", attr(unless(isImplicit())))); +} + TEST_P(ASTMatchersTest, NullStmt) { EXPECT_TRUE(matches("void f() {int i;;}", nullStmt())); EXPECT_TRUE(notMatches("void f() {int i;}", nullStmt())); Index: clang/unittests/AST/ASTTypeTraitsTest.cpp =================================================================== --- clang/unittests/AST/ASTTypeTraitsTest.cpp +++ clang/unittests/AST/ASTTypeTraitsTest.cpp @@ -120,6 +120,7 @@ VERIFY_NAME(CallExpr); VERIFY_NAME(Type); VERIFY_NAME(ConstantArrayType); + VERIFY_NAME(NonNullAttr); #undef VERIFY_NAME } @@ -148,6 +149,13 @@ nestedNameSpecifierLoc())); } +TEST(DynTypedNode, AttrSourceRange) { + RangeVerifier<DynTypedNode> Verifier; + Verifier.expectRange(1, 31, 1, 31); + EXPECT_TRUE(Verifier.match("void x(char *y __attribute__((nonnull)) );", + ast_matchers::attr())); +} + TEST(DynTypedNode, DeclDump) { DumpVerifier Verifier; Verifier.expectSubstring("FunctionDecl"); Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -141,6 +141,7 @@ REGISTER_MATCHER(asmStmt); REGISTER_MATCHER(atomicExpr); REGISTER_MATCHER(atomicType); + REGISTER_MATCHER(attr); REGISTER_MATCHER(autoType); REGISTER_MATCHER(autoreleasePoolStmt) REGISTER_MATCHER(binaryConditionalOperator); Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -544,6 +544,15 @@ } // namespace +bool HasNameMatcher::matchesNode(const NamedDecl &Node, StringRef Name) { + if (Name.contains("::")) // slow path + return HasNameMatcher({Name.str()}).matchesNode(Node); + // Fast path. + llvm::SmallString<128> Scratch; + StringRef NodeName = getNodeName(Node, Scratch); + return consumeNameSuffix(Name, NodeName) && Name.empty(); +} + bool HasNameMatcher::matchesNodeUnqualified(const NamedDecl &Node) const { assert(UseUnqualifiedMatch); llvm::SmallString<128> Scratch; @@ -973,6 +982,7 @@ const internal::VariadicAllOfMatcher<NestedNameSpecifier> nestedNameSpecifier; const internal::VariadicAllOfMatcher<NestedNameSpecifierLoc> nestedNameSpecifierLoc; +const internal::VariadicAllOfMatcher<Attr> attr; const internal::VariadicDynCastAllOfMatcher<Stmt, CUDAKernelCallExpr> cudaKernelCallExpr; const AstTypeMatcher<BuiltinType> builtinType; Index: clang/lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -131,6 +131,8 @@ else if (const TemplateArgumentLoc *TALoc = DynNode.get<TemplateArgumentLoc>()) traverse(*TALoc); + else if (const Attr *A = DynNode.get<Attr>()) + traverse(*A); // FIXME: Add other base types after adding tests. // It's OK to always overwrite the bound nodes, as if there was @@ -231,6 +233,14 @@ ScopedIncrement ScopedDepth(&CurrentDepth); return traverse(TAL); } + bool TraverseAttr(Attr *A) { + if (A->isImplicit() && + Finder->getASTContext().getParentMapContext().getTraversalKind() == + TK_IgnoreUnlessSpelledInSource) + return true; + ScopedIncrement ScopedDepth(&CurrentDepth); + return (A == nullptr || traverse(*A)); + } bool TraverseLambdaExpr(LambdaExpr *Node) { if (Finder->getASTContext().getParentMapContext().getTraversalKind() != TK_IgnoreUnlessSpelledInSource) @@ -314,6 +324,9 @@ bool baseTraverse(TemplateArgumentLoc TAL) { return VisitorBase::TraverseTemplateArgumentLoc(TAL); } + bool baseTraverse(const Attr &AttrNode) { + return VisitorBase::TraverseAttr(const_cast<Attr *>(&AttrNode)); + } // Sets 'Matched' to true if 'Matcher' matches 'Node' and: // 0 < CurrentDepth <= MaxDepth. @@ -458,6 +471,7 @@ bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS); bool TraverseConstructorInitializer(CXXCtorInitializer *CtorInit); bool TraverseTemplateArgumentLoc(TemplateArgumentLoc TAL); + bool TraverseAttr(Attr *AttrNode); // Matches children or descendants of 'Node' with 'BaseMatcher'. bool memoizedMatchesRecursively(const DynTypedNode &Node, ASTContext &Ctx, @@ -571,6 +585,8 @@ match(*N); } else if (auto *N = Node.get<TemplateArgumentLoc>()) { match(*N); + } else if (auto *N = Node.get<Attr>()) { + match(*N); } } @@ -697,6 +713,9 @@ void matchDispatch(const TemplateArgumentLoc *Node) { matchWithoutFilter(*Node, Matchers->TemplateArgumentLoc); } + void matchDispatch(const Attr *Node) { + matchWithoutFilter(*Node, Matchers->Attr); + } void matchDispatch(const void *) { /* Do nothing. */ } /// @} @@ -1068,6 +1087,11 @@ return RecursiveASTVisitor<MatchASTVisitor>::TraverseTemplateArgumentLoc(Loc); } +bool MatchASTVisitor::TraverseAttr(Attr *AttrNode) { + match(*AttrNode); + return RecursiveASTVisitor<MatchASTVisitor>::TraverseAttr(AttrNode); +} + class MatchASTConsumer : public ASTConsumer { public: MatchASTConsumer(MatchFinder *Finder, @@ -1150,6 +1174,12 @@ Matchers.AllCallbacks.insert(Action); } +void MatchFinder::addMatcher(const AttrMatcher &AttrMatch, + MatchCallback *Action) { + Matchers.Attr.emplace_back(AttrMatch, Action); + Matchers.AllCallbacks.insert(Action); +} + bool MatchFinder::addDynamicMatcher(const internal::DynTypedMatcher &NodeMatch, MatchCallback *Action) { if (NodeMatch.canConvertTo<Decl>()) { @@ -1176,6 +1206,9 @@ } else if (NodeMatch.canConvertTo<TemplateArgumentLoc>()) { addMatcher(NodeMatch.convertTo<TemplateArgumentLoc>(), Action); return true; + } else if (NodeMatch.canConvertTo<Attr>()) { + addMatcher(NodeMatch.convertTo<Attr>(), Action); + return true; } return false; } Index: clang/lib/AST/ASTTypeTraits.cpp =================================================================== --- clang/lib/AST/ASTTypeTraits.cpp +++ clang/lib/AST/ASTTypeTraits.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OpenMPClause.h" @@ -21,28 +22,31 @@ using namespace clang; const ASTNodeKind::KindInfo ASTNodeKind::AllKindInfo[] = { - { NKI_None, "<None>" }, - { NKI_None, "TemplateArgument" }, - { NKI_None, "TemplateArgumentLoc" }, - { NKI_None, "TemplateName" }, - { NKI_None, "NestedNameSpecifierLoc" }, - { NKI_None, "QualType" }, - { NKI_None, "TypeLoc" }, - { NKI_None, "CXXBaseSpecifier" }, - { NKI_None, "CXXCtorInitializer" }, - { NKI_None, "NestedNameSpecifier" }, - { NKI_None, "Decl" }, + {NKI_None, "<None>"}, + {NKI_None, "TemplateArgument"}, + {NKI_None, "TemplateArgumentLoc"}, + {NKI_None, "TemplateName"}, + {NKI_None, "NestedNameSpecifierLoc"}, + {NKI_None, "QualType"}, + {NKI_None, "TypeLoc"}, + {NKI_None, "CXXBaseSpecifier"}, + {NKI_None, "CXXCtorInitializer"}, + {NKI_None, "NestedNameSpecifier"}, + {NKI_None, "Decl"}, #define DECL(DERIVED, BASE) { NKI_##BASE, #DERIVED "Decl" }, #include "clang/AST/DeclNodes.inc" - { NKI_None, "Stmt" }, + {NKI_None, "Stmt"}, #define STMT(DERIVED, BASE) { NKI_##BASE, #DERIVED }, #include "clang/AST/StmtNodes.inc" - { NKI_None, "Type" }, + {NKI_None, "Type"}, #define TYPE(DERIVED, BASE) { NKI_##BASE, #DERIVED "Type" }, #include "clang/AST/TypeNodes.inc" - { NKI_None, "OMPClause" }, + {NKI_None, "OMPClause"}, #define OMP_CLAUSE_CLASS(Enum, Str, Class) {NKI_OMPClause, #Class}, #include "llvm/Frontend/OpenMP/OMPKinds.def" + {NKI_None, "Attr"}, +#define ATTR(A) {NKI_Attr, #A "Attr"}, +#include "clang/Basic/AttrList.inc" }; bool ASTNodeKind::isBaseOf(ASTNodeKind Other, unsigned *Distance) const { @@ -123,7 +127,17 @@ break; #include "llvm/Frontend/OpenMP/OMPKinds.def" } - llvm_unreachable("invalid stmt kind"); + llvm_unreachable("invalid omp clause kind"); +} + +ASTNodeKind ASTNodeKind::getFromNode(const Attr &A) { + switch (A.getKind()) { +#define ATTR(A) \ + case attr::A: \ + return ASTNodeKind(NKI_##A##Attr); +#include "clang/Basic/AttrList.inc" + } + llvm_unreachable("invalid attr kind"); } void DynTypedNode::print(llvm::raw_ostream &OS, @@ -151,6 +165,8 @@ S->printPretty(OS, nullptr, PP); else if (const Type *T = get<Type>()) QualType(T, 0).print(OS, PP); + else if (const Attr *A = get<Attr>()) + A->printPretty(OS, PP); else OS << "Unable to print values of type " << NodeKind.asStringRef() << "\n"; } @@ -182,5 +198,7 @@ return TAL->getSourceRange(); if (const auto *C = get<OMPClause>()) return SourceRange(C->getBeginLoc(), C->getEndLoc()); + if (const auto *A = get<Attr>()) + return A->getRange(); return SourceRange(); } Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -726,7 +726,9 @@ bool matchesNode(const NamedDecl &Node) const override; - private: + static bool matchesNode(const NamedDecl &Node, StringRef Name); + +private: /// Unqualified match routine. /// /// It is much faster than the full match, but it only works for unqualified @@ -944,7 +946,8 @@ std::is_same<T, NestedNameSpecifier>::value || std::is_same<T, NestedNameSpecifierLoc>::value || std::is_same<T, CXXCtorInitializer>::value || - std::is_same<T, TemplateArgumentLoc>::value; + std::is_same<T, TemplateArgumentLoc>::value || + std::is_same<T, Attr>::value; }; template <typename T> const bool IsBaseType<T>::value; Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -146,6 +146,7 @@ using NestedNameSpecifierLocMatcher = internal::Matcher<NestedNameSpecifierLoc>; using CXXCtorInitializerMatcher = internal::Matcher<CXXCtorInitializer>; using TemplateArgumentLocMatcher = internal::Matcher<TemplateArgumentLoc>; +using AttrMatcher = internal::Matcher<Attr>; /// @} /// Matches any node. @@ -732,9 +733,10 @@ InnerMatcher.matches(*Decl, Finder, Builder)); } -/// Matches a declaration that has been implicitly added -/// by the compiler (eg. implicit default/copy constructors). -AST_MATCHER(Decl, isImplicit) { +/// Matches an entity that has been implicitly added by the compiler (e.g. +/// implicit default/copy constructors). +AST_POLYMORPHIC_MATCHER(isImplicit, + AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Attr)) { return Node.isImplicit(); } @@ -6728,6 +6730,40 @@ return InnerMatcher.matches(*Node.getAsNamespace(), Finder, Builder); } +/// Matches attributes. +/// Attributes may be attached with a variety of different syntaxes (including +/// keywords, C++11 attributes, GNU ``__attribute``` and MSVC `__declspec``, +/// and ``#pragma``s). They may also be implicit. +/// +/// Given +/// \code +/// struct [[nodiscard]] Foo{}; +/// void bar(int * __attribute__((nonnull)) ); +/// __declspec(noinline) void baz(); +/// +/// #pragma omp declare simd +/// int min(); +/// \endcode +/// attr() +/// matches "nodiscard", "nonnull", "noinline", and the whole "#pragma" line. +extern const internal::VariadicAllOfMatcher<Attr> attr; + +/// Matches Attr nodes that have the specified attribute name. +/// +/// The attribute name is the token in the source code naming the attribute. +/// Synonyms are not supported and scope qualifiers are ignored. +/// +/// Given +/// \code +/// alignas(16) int bar; +/// struct [[clang::warn_unused_result]] Foo{}; +/// \endcode +/// matches hasAttrName("alignas") and "warn_unused_result" +/// (but not "nodiscard" or "clang::warn_unused_result"). +AST_MATCHER_P(Attr, hasAttrName, llvm::StringRef, Name) { + return Node.getAttrName() && Node.getAttrName()->isStr(Name); +} + /// Overloads for the \c equalsNode matcher. /// FIXME: Implement for other node types. /// @{ Index: clang/include/clang/ASTMatchers/ASTMatchFinder.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchFinder.h +++ clang/include/clang/ASTMatchers/ASTMatchFinder.h @@ -161,6 +161,7 @@ MatchCallback *Action); void addMatcher(const TemplateArgumentLocMatcher &NodeMatch, MatchCallback *Action); + void addMatcher(const AttrMatcher &NodeMatch, MatchCallback *Action); /// @} /// Adds a matcher to execute when running over the AST. @@ -213,6 +214,7 @@ std::vector<std::pair<CXXCtorInitializerMatcher, MatchCallback *>> CtorInit; std::vector<std::pair<TemplateArgumentLocMatcher, MatchCallback *>> TemplateArgumentLoc; + std::vector<std::pair<AttrMatcher, MatchCallback *>> Attr; /// All the callbacks in one container to simplify iteration. llvm::SmallPtrSet<MatchCallback *, 16> AllCallbacks; }; Index: clang/include/clang/AST/ASTTypeTraits.h =================================================================== --- clang/include/clang/AST/ASTTypeTraits.h +++ clang/include/clang/AST/ASTTypeTraits.h @@ -25,10 +25,8 @@ #include "llvm/Support/AlignOf.h" namespace llvm { - class raw_ostream; - -} +} // namespace llvm namespace clang { @@ -70,6 +68,7 @@ static ASTNodeKind getFromNode(const Stmt &S); static ASTNodeKind getFromNode(const Type &T); static ASTNodeKind getFromNode(const OMPClause &C); + static ASTNodeKind getFromNode(const Attr &A); /// \} /// Returns \c true if \c this and \c Other represent the same kind. @@ -153,6 +152,9 @@ NKI_OMPClause, #define OMP_CLAUSE_CLASS(Enum, Str, Class) NKI_##Class, #include "llvm/Frontend/OpenMP/OMPKinds.def" + NKI_Attr, +#define ATTR(A) NKI_##A##Attr, +#include "clang/Basic/AttrList.inc" NKI_NumberOfKinds }; @@ -202,6 +204,7 @@ KIND_TO_KIND_ID(Stmt) KIND_TO_KIND_ID(Type) KIND_TO_KIND_ID(OMPClause) +KIND_TO_KIND_ID(Attr) KIND_TO_KIND_ID(CXXBaseSpecifier) #define DECL(DERIVED, BASE) KIND_TO_KIND_ID(DERIVED##Decl) #include "clang/AST/DeclNodes.inc" @@ -211,6 +214,8 @@ #include "clang/AST/TypeNodes.inc" #define OMP_CLAUSE_CLASS(Enum, Str, Class) KIND_TO_KIND_ID(Class) #include "llvm/Frontend/OpenMP/OMPKinds.def" +#define ATTR(A) KIND_TO_KIND_ID(A##Attr) +#include "clang/Basic/AttrList.inc" #undef KIND_TO_KIND_ID inline raw_ostream &operator<<(raw_ostream &OS, ASTNodeKind K) { @@ -487,6 +492,11 @@ T, std::enable_if_t<std::is_base_of<OMPClause, T>::value>> : public DynCastPtrConverter<T, OMPClause> {}; +template <typename T> +struct DynTypedNode::BaseConverter< + T, std::enable_if_t<std::is_base_of<Attr, T>::value>> + : public DynCastPtrConverter<T, Attr> {}; + template <> struct DynTypedNode::BaseConverter< NestedNameSpecifier, void> : public PtrConverter<NestedNameSpecifier> {}; Index: clang/include/clang/AST/ASTFwd.h =================================================================== --- clang/include/clang/AST/ASTFwd.h +++ clang/include/clang/AST/ASTFwd.h @@ -29,7 +29,9 @@ class OMPClause; #define OMP_CLAUSE_CLASS(Enum, Str, Class) class Class; #include "llvm/Frontend/OpenMP/OMPKinds.def" - +class Attr; +#define ATTR(A) class A##Attr; +#include "clang/Basic/AttrList.inc" } // end namespace clang Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -100,6 +100,24 @@ <tr style="text-align:left"><th>Return type</th><th>Name</th><th>Parameters</th></tr> <!-- START_DECL_MATCHERS --> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Attr.html">Attr</a>></td><td class="name" onclick="toggle('attr0')"><a name="attr0Anchor">attr</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Attr.html">Attr</a>>...</td></tr> +<tr><td colspan="4" class="doc" id="attr0"><pre>Matches attributes. +Attributes may be attached with a variety of different syntaxes (including +keywords, C++11 attributes, GNU ``__attribute``` and MSVC `__declspec``, +and ``#pragma``s). They may also be implicit. + +Given + struct [[nodiscard]] Foo{}; + void bar(int * __attribute__((nonnull)) ); + __declspec(noinline) void baz(); + + #pragma omp declare simd + int min(); +attr() + matches "nodiscard", "nonnull", "noinline", and the whole "#pragma" line. +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXCtorInitializer.html">CXXCtorInitializer</a>></td><td class="name" onclick="toggle('cxxCtorInitializer0')"><a name="cxxCtorInitializer0Anchor">cxxCtorInitializer</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXCtorInitializer.html">CXXCtorInitializer</a>>...</td></tr> <tr><td colspan="4" class="doc" id="cxxCtorInitializer0"><pre>Matches constructor initializers. @@ -2168,6 +2186,26 @@ </pre></td></tr> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Attr.html">Attr</a>></td><td class="name" onclick="toggle('hasAttrName0')"><a name="hasAttrName0Anchor">hasAttrName</a></td><td>llvm::StringRef Name</td></tr> +<tr><td colspan="4" class="doc" id="hasAttrName0"><pre>Matches Attr nodes that have the specified attribute name. + +The attribute name is the token in the source code naming the attribute. +Synonyms are not supported and scope qualifiers are ignored. + +Given + alignas(16) int bar; + struct [[clang::warn_unused_result]] Foo{}; +matches hasAttrName("alignas") and "warn_unused_result" +(but not "nodiscard" or "clang::warn_unused_result"). +</pre></td></tr> + + +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Attr.html">Attr</a>></td><td class="name" onclick="toggle('isImplicit1')"><a name="isImplicit1Anchor">isImplicit</a></td><td></td></tr> +<tr><td colspan="4" class="doc" id="isImplicit1"><pre>Matches an entity that has been implicitly added by the compiler (e.g. +implicit default/copy constructors). +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName0')"><a name="hasAnyOperatorName0Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr> <tr><td colspan="4" class="doc" id="hasAnyOperatorName0"><pre>Matches operator expressions (binary or unary) that have any of the specified names. @@ -3090,8 +3128,8 @@ <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('isImplicit0')"><a name="isImplicit0Anchor">isImplicit</a></td><td></td></tr> -<tr><td colspan="4" class="doc" id="isImplicit0"><pre>Matches a declaration that has been implicitly added -by the compiler (eg. implicit default/copy constructors). +<tr><td colspan="4" class="doc" id="isImplicit0"><pre>Matches an entity that has been implicitly added by the compiler (e.g. +implicit default/copy constructors). </pre></td></tr>
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits