Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -2416,10 +2416,11 @@ // These versions of the constructor are for derived classes. CallExpr(const ASTContext &C, StmtClass SC, Expr *fn, ArrayRef preargs, ArrayRef args, QualType t, - ExprValueKind VK, SourceLocation rparenloc, unsigned MinNumArgs = 0); + ExprValueKind VK, SourceLocation rparenloc, unsigned MinNumArgs = 0, + bool UsesADL = false); CallExpr(const ASTContext &C, StmtClass SC, Expr *fn, ArrayRef args, QualType t, ExprValueKind VK, SourceLocation rparenloc, - unsigned MinNumArgs = 0); + unsigned MinNumArgs = 0, bool UsesADL = false); CallExpr(const ASTContext &C, StmtClass SC, unsigned NumPreArgs, unsigned NumArgs, EmptyShell Empty); @@ -2443,7 +2444,8 @@ /// arguments. The actual number of arguments will be the greater of /// args.size() and MinNumArgs. CallExpr(const ASTContext &C, Expr *fn, ArrayRef args, QualType t, - ExprValueKind VK, SourceLocation rparenloc, unsigned MinNumArgs = 0); + ExprValueKind VK, SourceLocation rparenloc, unsigned MinNumArgs = 0, + bool UsesADL = false); /// Build an empty call expression. CallExpr(const ASTContext &C, unsigned NumArgs, EmptyShell Empty); @@ -2452,6 +2454,9 @@ Expr *getCallee() { return cast(SubExprs[FN]); } void setCallee(Expr *F) { SubExprs[FN] = F; } + bool usesADL() const { return CallExprBits.UsesADL; } + void setUsesADL(bool V = true) { CallExprBits.UsesADL = V; } + Decl *getCalleeDecl(); const Decl *getCalleeDecl() const { return const_cast(this)->getCalleeDecl(); Index: include/clang/AST/ExprCXX.h =================================================================== --- include/clang/AST/ExprCXX.h +++ include/clang/AST/ExprCXX.h @@ -90,10 +90,12 @@ friend class ASTStmtReader; friend class ASTStmtWriter; - CXXOperatorCallExpr(ASTContext& C, OverloadedOperatorKind Op, Expr *fn, - ArrayRef args, QualType t, ExprValueKind VK, - SourceLocation operatorloc, FPOptions FPFeatures) - : CallExpr(C, CXXOperatorCallExprClass, fn, args, t, VK, operatorloc), + CXXOperatorCallExpr(ASTContext &C, OverloadedOperatorKind Op, Expr *fn, + ArrayRef args, QualType t, ExprValueKind VK, + SourceLocation operatorloc, FPOptions FPFeatures, + bool UsesADL = false) + : CallExpr(C, CXXOperatorCallExprClass, fn, args, t, VK, operatorloc, + /*MinNumArgs=*/0, UsesADL), Operator(Op), FPFeatures(FPFeatures) { Range = getSourceRangeImpl(); } @@ -168,7 +170,8 @@ CXXMemberCallExpr(ASTContext &C, Expr *fn, ArrayRef args, QualType t, ExprValueKind VK, SourceLocation RP, unsigned MinNumArgs = 0) - : CallExpr(C, CXXMemberCallExprClass, fn, args, t, VK, RP, MinNumArgs) {} + : CallExpr(C, CXXMemberCallExprClass, fn, args, t, VK, RP, MinNumArgs, + /*UsesADL=*/false) {} CXXMemberCallExpr(ASTContext &C, unsigned NumArgs, EmptyShell Empty) : CallExpr(C, CXXMemberCallExprClass, /*NumPreArgs=*/0, NumArgs, Empty) {} @@ -212,7 +215,7 @@ ArrayRef args, QualType t, ExprValueKind VK, SourceLocation RP, unsigned MinNumArgs = 0) : CallExpr(C, CUDAKernelCallExprClass, fn, Config, args, t, VK, RP, - MinNumArgs) {} + MinNumArgs, /*UsesADL=*/false) {} CUDAKernelCallExpr(ASTContext &C, unsigned NumArgs, EmptyShell Empty) : CallExpr(C, CUDAKernelCallExprClass, /*NumPreArgs=*/END_PREARG, NumArgs, @@ -487,16 +490,17 @@ friend class ASTStmtReader; friend class ASTStmtWriter; - UserDefinedLiteral(const ASTContext &C, Expr *Fn, ArrayRef Args, + UserDefinedLiteral(const ASTContext &C, Expr *Fn, ArrayRef Args, QualType T, ExprValueKind VK, SourceLocation LitEndLoc, SourceLocation SuffixLoc) - : CallExpr(C, UserDefinedLiteralClass, Fn, Args, T, VK, LitEndLoc), + : CallExpr(C, UserDefinedLiteralClass, Fn, Args, T, VK, LitEndLoc, + /*MinNumArgs=*/0, /*UsesADL=*/false), UDSuffixLoc(SuffixLoc) {} explicit UserDefinedLiteral(const ASTContext &C, unsigned NumArgs, EmptyShell Empty) - : CallExpr(C, UserDefinedLiteralClass, /*NumPreArgs=*/0, NumArgs, - Empty) {} + : CallExpr(C, UserDefinedLiteralClass, /*NumPreArgs=*/0, NumArgs, Empty) { + } /// The kind of literal operator which is invoked. enum LiteralOperatorKind { Index: include/clang/AST/Stmt.h =================================================================== --- include/clang/AST/Stmt.h +++ include/clang/AST/Stmt.h @@ -430,6 +430,9 @@ unsigned : NumExprBits; unsigned NumPreArgs : 1; + + /// True if the callee of the call expression was found using ADL. + unsigned UsesADL : 1; }; class MemberExprBitfields { Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -1257,6 +1257,26 @@ /// \endcode extern const internal::VariadicDynCastAllOfMatcher callExpr; +/// Matches call expressions which were resolved using ADL. +/// +/// Example matches y(x) but not y(42) or NS::y(x). +/// \code +/// namespace NS { +/// struct X {}; +/// void y(X); +/// } // namespace NS +/// +/// void y(...); +/// +/// void test() { +/// NS::X x; +/// y(x); // Matches +/// NS::y(x); // Doesn't match +/// y(42); // Doesn't match. +/// } +/// \endcode +AST_MATCHER(CallExpr, usesADL) { return Node.usesADL(); } + /// Matches lambda expressions. /// /// Example matches [&](){return 5;} Index: include/clang/Sema/Overload.h =================================================================== --- include/clang/Sema/Overload.h +++ include/clang/Sema/Overload.h @@ -771,6 +771,9 @@ /// object argument. bool IgnoreObjectArgument; + /// True if the candidate was found using ADL. + bool IsADLCandidate; + /// FailureKind - The reason why this candidate is not viable. /// Actually an OverloadFailureKind. unsigned char FailureKind; @@ -823,6 +826,11 @@ return Function->getNumParams(); return ExplicitCallArguments; } + + private: + // Only the OverloadCandidate set is allowed to construct OverloadCandidates. + friend class OverloadCandidateSet; + OverloadCandidate() = default; }; /// OverloadCandidateSet - A set of overload candidates, used in C++ @@ -945,6 +953,7 @@ Candidates.push_back(OverloadCandidate()); OverloadCandidate &C = Candidates.back(); + C.IsADLCandidate = false; C.Conversions = Conversions.empty() ? allocateConversionSequences(NumConversions) : Conversions; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2749,13 +2749,13 @@ typedef llvm::SmallSetVector AssociatedNamespaceSet; typedef llvm::SmallSetVector AssociatedClassSet; - void AddOverloadCandidate(FunctionDecl *Function, - DeclAccessPair FoundDecl, + void AddOverloadCandidate(FunctionDecl *Function, DeclAccessPair FoundDecl, ArrayRef Args, OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false, bool PartialOverloading = false, bool AllowExplicit = false, + bool IsADLCandidate = false, ConversionSequenceList EarlyConversions = None); void AddFunctionCandidates(const UnresolvedSetImpl &Functions, ArrayRef Args, @@ -2789,13 +2789,11 @@ OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false, bool PartialOverloading = false); - void AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, - DeclAccessPair FoundDecl, - TemplateArgumentListInfo *ExplicitTemplateArgs, - ArrayRef Args, - OverloadCandidateSet& CandidateSet, - bool SuppressUserConversions = false, - bool PartialOverloading = false); + void AddTemplateOverloadCandidate( + FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, + TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, + OverloadCandidateSet &CandidateSet, bool SuppressUserConversions = false, + bool PartialOverloading = false, bool IsADLCandidate = false); bool CheckNonDependentConversions(FunctionTemplateDecl *FunctionTemplate, ArrayRef ParamTypes, ArrayRef Args, @@ -4387,12 +4385,11 @@ MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig = nullptr, bool IsExecConfig = false); - ExprResult BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, - SourceLocation LParenLoc, - ArrayRef Arg, - SourceLocation RParenLoc, - Expr *Config = nullptr, - bool IsExecConfig = false); + ExprResult + BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, SourceLocation LParenLoc, + ArrayRef Arg, SourceLocation RParenLoc, + Expr *Config = nullptr, bool IsExecConfig = false, + bool UsesADL = false); ExprResult ActOnCUDAExecConfigExpr(Scope *S, SourceLocation LLLLoc, MultiExprArg ExecConfig, Index: lib/AST/ASTDumper.cpp =================================================================== --- lib/AST/ASTDumper.cpp +++ lib/AST/ASTDumper.cpp @@ -382,6 +382,7 @@ void VisitOMPExecutableDirective(const OMPExecutableDirective *Node); // Exprs + void VisitCallExpr(const CallExpr *Node); void VisitCastExpr(const CastExpr *Node); void VisitImplicitCastExpr(const ImplicitCastExpr *Node); void VisitDeclRefExpr(const DeclRefExpr *Node); @@ -1876,6 +1877,11 @@ OS << ')'; } +void ASTDumper::VisitCallExpr(const CallExpr *Node) { + if (Node->usesADL()) + OS << " adl"; +} + void ASTDumper::VisitCastExpr(const CastExpr *Node) { OS << " <"; { Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -7380,7 +7380,7 @@ if (const auto *OCE = dyn_cast(E)) { return new (Importer.getToContext()) CXXOperatorCallExpr( Importer.getToContext(), OCE->getOperator(), ToCallee, ToArgs, ToType, - OCE->getValueKind(), ToRParenLoc, OCE->getFPFeatures()); + OCE->getValueKind(), ToRParenLoc, OCE->getFPFeatures(), OCE->usesADL()); } return new (Importer.getToContext()) CallExpr( Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -1223,11 +1223,13 @@ CallExpr::CallExpr(const ASTContext &C, StmtClass SC, Expr *fn, ArrayRef preargs, ArrayRef args, QualType t, ExprValueKind VK, SourceLocation rparenloc, - unsigned MinNumArgs) + unsigned MinNumArgs, bool UsesADL) : Expr(SC, t, VK, OK_Ordinary, fn->isTypeDependent(), fn->isValueDependent(), fn->isInstantiationDependent(), fn->containsUnexpandedParameterPack()), RParenLoc(rparenloc) { + CallExprBits.UsesADL = UsesADL; + NumArgs = std::max(args.size(), MinNumArgs); unsigned NumPreArgs = preargs.size(); CallExprBits.NumPreArgs = NumPreArgs; @@ -1249,19 +1251,20 @@ CallExpr::CallExpr(const ASTContext &C, StmtClass SC, Expr *fn, ArrayRef args, QualType t, ExprValueKind VK, - SourceLocation rparenloc, unsigned MinNumArgs) + SourceLocation rparenloc, unsigned MinNumArgs, bool UsesADL) : CallExpr(C, SC, fn, ArrayRef(), args, t, VK, rparenloc, - MinNumArgs) {} + MinNumArgs, UsesADL) {} CallExpr::CallExpr(const ASTContext &C, Expr *fn, ArrayRef args, QualType t, ExprValueKind VK, SourceLocation rparenloc, - unsigned MinNumArgs) - : CallExpr(C, CallExprClass, fn, ArrayRef(), args, t, VK, - rparenloc, MinNumArgs) {} + unsigned MinNumArgs, bool UsesADL) + : CallExpr(C, CallExprClass, fn, ArrayRef(), args, t, VK, rparenloc, + MinNumArgs, UsesADL) {} CallExpr::CallExpr(const ASTContext &C, StmtClass SC, unsigned NumPreArgs, unsigned NumArgs, EmptyShell Empty) : Expr(SC, Empty), NumArgs(NumArgs) { + CallExprBits.UsesADL = false; CallExprBits.NumPreArgs = NumPreArgs; SubExprs = new (C) Stmt *[NumArgs + PREARGS_START + NumPreArgs]; } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -5585,12 +5585,11 @@ /// block-pointer type. /// /// \param NDecl the declaration being called, if available -ExprResult -Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, - SourceLocation LParenLoc, - ArrayRef Args, - SourceLocation RParenLoc, - Expr *Config, bool IsExecConfig) { +ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, + SourceLocation LParenLoc, + ArrayRef Args, + SourceLocation RParenLoc, Expr *Config, + bool IsExecConfig, bool UsesADL) { FunctionDecl *FDecl = dyn_cast_or_null(NDecl); unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0); @@ -5670,13 +5669,13 @@ unsigned NumParams = Proto ? Proto->getNumParams() : 0; CallExpr *TheCall; - if (Config) + if (Config) { TheCall = new (Context) CUDAKernelCallExpr(Context, Fn, cast(Config), Args, ResultTy, VK_RValue, RParenLoc, NumParams); - else - TheCall = new (Context) - CallExpr(Context, Fn, Args, ResultTy, VK_RValue, RParenLoc, NumParams); + } else + TheCall = new (Context) CallExpr(Context, Fn, Args, ResultTy, VK_RValue, + RParenLoc, NumParams, UsesADL); if (!getLangOpts().CPlusPlus) { // C cannot always handle TypoExpr nodes in builtin calls and direct Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -5946,15 +5946,13 @@ /// \param PartialOverloading true if we are performing "partial" overloading /// based on an incomplete set of function arguments. This feature is used by /// code completion. -void -Sema::AddOverloadCandidate(FunctionDecl *Function, - DeclAccessPair FoundDecl, - ArrayRef Args, - OverloadCandidateSet &CandidateSet, - bool SuppressUserConversions, - bool PartialOverloading, - bool AllowExplicit, - ConversionSequenceList EarlyConversions) { +void Sema::AddOverloadCandidate(FunctionDecl *Function, + DeclAccessPair FoundDecl, ArrayRef Args, + OverloadCandidateSet &CandidateSet, + bool SuppressUserConversions, + bool PartialOverloading, bool AllowExplicit, + bool IsADLCandidate, + ConversionSequenceList EarlyConversions) { const FunctionProtoType *Proto = dyn_cast(Function->getType()->getAs()); assert(Proto && "Functions without a prototype cannot be overloaded"); @@ -6013,6 +6011,7 @@ Candidate.Function = Function; Candidate.Viable = true; Candidate.IsSurrogate = false; + Candidate.IsADLCandidate = IsADLCandidate; Candidate.IgnoreObjectArgument = false; Candidate.ExplicitCallArguments = Args.size(); @@ -6715,14 +6714,11 @@ /// Add a C++ function template specialization as a candidate /// in the candidate set, using template argument deduction to produce /// an appropriate function template specialization. -void -Sema::AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, - DeclAccessPair FoundDecl, - TemplateArgumentListInfo *ExplicitTemplateArgs, - ArrayRef Args, - OverloadCandidateSet& CandidateSet, - bool SuppressUserConversions, - bool PartialOverloading) { +void Sema::AddTemplateOverloadCandidate( + FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl, + TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef Args, + OverloadCandidateSet &CandidateSet, bool SuppressUserConversions, + bool PartialOverloading, bool IsADLCandidate) { if (!CandidateSet.isNewCandidate(FunctionTemplate)) return; @@ -6751,6 +6747,7 @@ Candidate.Function = FunctionTemplate->getTemplatedDecl(); Candidate.Viable = false; Candidate.IsSurrogate = false; + Candidate.IsADLCandidate = IsADLCandidate; // Ignore the object argument if there is one, since we don't have an object // type. Candidate.IgnoreObjectArgument = @@ -6772,7 +6769,7 @@ assert(Specialization && "Missing function template specialization?"); AddOverloadCandidate(Specialization, FoundDecl, Args, CandidateSet, SuppressUserConversions, PartialOverloading, - /*AllowExplicit*/false, Conversions); + /*AllowExplicit*/ false, IsADLCandidate, Conversions); } /// Check that implicit conversion sequences can be formed for each argument @@ -8935,16 +8932,19 @@ // set. for (ADLResult::iterator I = Fns.begin(), E = Fns.end(); I != E; ++I) { DeclAccessPair FoundDecl = DeclAccessPair::make(*I, AS_none); + if (FunctionDecl *FD = dyn_cast(*I)) { if (ExplicitTemplateArgs) continue; AddOverloadCandidate(FD, FoundDecl, Args, CandidateSet, false, - PartialOverloading); + PartialOverloading, /*AllowExplicit=*/false, + /*IsADLCandidate=*/true); } else - AddTemplateOverloadCandidate(cast(*I), - FoundDecl, ExplicitTemplateArgs, - Args, CandidateSet, PartialOverloading); + AddTemplateOverloadCandidate(cast(*I), FoundDecl, + ExplicitTemplateArgs, Args, CandidateSet, + /*SuppressUserConversions=*/false, + PartialOverloading, /*IsADLCandidate=*/true); } } @@ -12021,7 +12021,8 @@ return ExprError(); Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl); return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, RParenLoc, - ExecConfig); + ExecConfig, /*IsExecConfig=*/false, + (*Best)->IsADLCandidate); } case OR_No_Viable_Function: { @@ -12073,7 +12074,8 @@ FunctionDecl *FDecl = (*Best)->Function; Fn = SemaRef.FixOverloadedFunctionReference(Fn, (*Best)->FoundDecl, FDecl); return SemaRef.BuildResolvedCallExpr(Fn, FDecl, LParenLoc, Args, RParenLoc, - ExecConfig); + ExecConfig, /*IsExecConfig=*/false, + (*Best)->IsADLCandidate); } } @@ -12262,9 +12264,9 @@ ResultTy = ResultTy.getNonLValueExprType(Context); Args[0] = Input; - CallExpr *TheCall = - new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.get(), ArgsArray, - ResultTy, VK, OpLoc, FPOptions()); + CallExpr *TheCall = new (Context) + CXXOperatorCallExpr(Context, Op, FnExpr.get(), ArgsArray, ResultTy, + VK, OpLoc, FPOptions(), Best->IsADLCandidate); if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl)) return ExprError(); @@ -12494,10 +12496,9 @@ ExprValueKind VK = Expr::getValueKindForType(ResultTy); ResultTy = ResultTy.getNonLValueExprType(Context); - CXXOperatorCallExpr *TheCall = - new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.get(), - Args, ResultTy, VK, OpLoc, - FPFeatures); + CXXOperatorCallExpr *TheCall = new (Context) + CXXOperatorCallExpr(Context, Op, FnExpr.get(), Args, ResultTy, VK, + OpLoc, FPFeatures, Best->IsADLCandidate); if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl)) Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -737,6 +737,7 @@ E->setCallee(Record.readSubExpr()); for (unsigned I = 0; I != NumArgs; ++I) E->setArg(I, Record.readSubExpr()); + E->setUsesADL(Record.readInt()); } void ASTStmtReader::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -651,6 +651,7 @@ for (CallExpr::arg_iterator Arg = E->arg_begin(), ArgEnd = E->arg_end(); Arg != ArgEnd; ++Arg) Record.AddStmt(*Arg); + Record.push_back(E->usesADL()); Code = serialization::EXPR_CALL; } Index: test/AST/ast-dump-expr.cpp =================================================================== --- /dev/null +++ test/AST/ast-dump-expr.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-unused-value -std=c++14 -ast-dump %s \ +// RUN: | FileCheck -strict-whitespace %s + +namespace NS { +struct X {}; +void f(X); +void y(...); +} // namespace NS + +// CHECK-LABEL: FunctionDecl 0x{{[^ ]*}} {{.*}}ADLCall 'void ()' +void ADLCall() { + NS::X x; + // CHECK: CallExpr 0x{{[^ ]*}} ]+}}> 'void' adl{{$}} + f(x); + // CHECK: CallExpr 0x{{[^ ]*}} ]+}}> 'void' adl{{$}} + y(x); +} + +// CHECK-LABEL: FunctionDecl 0x{{[^ ]*}} {{.*}}NonADLCall 'void ()' +void NonADLCall() { + NS::X x; + // CHECK: CallExpr 0x{{[^ ]*}} ]+}}> 'void'{{$}} + NS::f(x); +} + +// CHECK-LABEL: FunctionDecl 0x{{[^ ]*}} {{.*}}NonADLCall2 'void ()' +void NonADLCall2() { + NS::X x; + using NS::f; + // CHECK: CallExpr 0x{{[^ ]*}} ]+}}> 'void'{{$}} + f(x); + // CHECK: CallExpr 0x{{[^ ]*}} ]+}}> 'void' adl{{$}} + y(x); +} + +namespace test_adl_call_three { +using namespace NS; +// CHECK-LABEL: FunctionDecl 0x{{[^ ]*}} {{.*}}NonADLCall3 'void ()' +void NonADLCall3() { + X x; + // CHECK: CallExpr 0x{{[^ ]*}} ]+}}> 'void'{{$}} + f(x); +} +} // namespace test_adl_call_three Index: unittests/ASTMatchers/ASTMatchersNodeTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -199,6 +199,40 @@ "-fno-delayed-template-parsing")); } +TEST(Matcher, ADLCall) { + StatementMatcher ADLMatch = callExpr(usesADL()); + StatementMatcher ADLMatchOper = cxxOperatorCallExpr(usesADL()); + auto NS_Str = R"cpp( + namespace NS { + struct X {}; + void f(X); + void operator+(X, X); + } + struct MyX {}; + void f(...); + void operator+(MyX, MyX); +)cpp"; + + auto MkStr = [&](std::string Body) -> std::string { + std::string S = NS_Str; + S += "void test_fn() { " + Body + " }"; + return S; + }; + + EXPECT_TRUE(matches(MkStr("NS::X x; f(x);"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("NS::X x; NS::f(x);"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("MyX x; f(x);"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("NS::X x; using NS::f; f(x);"), ADLMatch)); + + // Operator call expressions + EXPECT_TRUE(matches(MkStr("NS::X x; x + x;"), ADLMatch)); + EXPECT_TRUE(matches(MkStr("NS::X x; x + x;"), ADLMatchOper)); + EXPECT_TRUE(notMatches(MkStr("MyX x; x + x;"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("MyX x; x + x;"), ADLMatchOper)); + EXPECT_TRUE(matches(MkStr("NS::X x; operator+(x, x);"), ADLMatch)); + EXPECT_TRUE(notMatches(MkStr("NS::X x; NS::operator+(x, x);"), ADLMatch)); +} + TEST(Matcher, Call) { // FIXME: Do we want to overload Call() to directly take // Matcher, too?