It looks like there are paths through MatchTemplateParametersToScopeSpecifier that return nullptr on invalid inputs but don't set Invalid to true; I don't think this patch is sufficient, though it does seem correct as far as it goes.
On Wed, Jan 28, 2015 at 8:14 PM, Nico Weber <[email protected]> wrote: > On Wed, Apr 16, 2014 at 8:29 PM, Richard Smith <[email protected] > > wrote: > >> Author: rsmith >> Date: Wed Apr 16 22:29:33 2014 >> New Revision: 206442 >> >> URL: http://llvm.org/viewvc/llvm-project?rev=206442&view=rev >> Log: >> Refactor all the checking for missing 'template<>'s when a declaration >> has a >> template-id after its scope specifier into a single place. >> >> Modified: >> cfe/trunk/include/clang/Sema/Sema.h >> cfe/trunk/lib/Parse/ParseDeclCXX.cpp >> cfe/trunk/lib/Sema/SemaDecl.cpp >> cfe/trunk/lib/Sema/SemaDeclCXX.cpp >> cfe/trunk/lib/Sema/SemaTemplate.cpp >> cfe/trunk/test/FixIt/fixit.cpp >> >> Modified: cfe/trunk/include/clang/Sema/Sema.h >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=206442&r1=206441&r2=206442&view=diff >> >> ============================================================================== >> --- cfe/trunk/include/clang/Sema/Sema.h (original) >> +++ cfe/trunk/include/clang/Sema/Sema.h Wed Apr 16 22:29:33 2014 >> @@ -5197,7 +5197,8 @@ public: >> TemplateParamListContext TPC); >> TemplateParameterList *MatchTemplateParametersToScopeSpecifier( >> SourceLocation DeclStartLoc, SourceLocation DeclLoc, >> - const CXXScopeSpec &SS, ArrayRef<TemplateParameterList *> >> ParamLists, >> + const CXXScopeSpec &SS, TemplateIdAnnotation *TemplateId, >> + ArrayRef<TemplateParameterList *> ParamLists, >> bool IsFriend, bool &IsExplicitSpecialization, bool &Invalid); >> >> DeclResult CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind >> TUK, >> @@ -5279,12 +5280,7 @@ public: >> ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, >> TagUseKind TUK, >> SourceLocation KWLoc, >> SourceLocation ModulePrivateLoc, >> - CXXScopeSpec &SS, >> - TemplateTy Template, >> - SourceLocation TemplateNameLoc, >> - SourceLocation LAngleLoc, >> - ASTTemplateArgsPtr TemplateArgs, >> - SourceLocation RAngleLoc, >> + TemplateIdAnnotation &TemplateId, >> AttributeList *Attr, >> MultiTemplateParamsArg >> TemplateParameterLists); >> >> >> Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=206442&r1=206441&r2=206442&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original) >> +++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed Apr 16 22:29:33 2014 >> @@ -1541,18 +1541,11 @@ void Parser::ParseClassSpecifier(tok::To >> } >> >> // Build the class template specialization. >> - TagOrTempResult >> - = Actions.ActOnClassTemplateSpecialization(getCurScope(), >> TagType, TUK, >> - StartLoc, DS.getModulePrivateSpecLoc(), SS, >> - TemplateId->Template, >> - TemplateId->TemplateNameLoc, >> - TemplateId->LAngleLoc, >> - TemplateArgsPtr, >> - TemplateId->RAngleLoc, >> - attrs.getList(), >> - MultiTemplateParamsArg( >> - TemplateParams? >> &(*TemplateParams)[0] : 0, >> - TemplateParams? TemplateParams->size() >> : 0)); >> + TagOrTempResult = Actions.ActOnClassTemplateSpecialization( >> + getCurScope(), TagType, TUK, StartLoc, >> DS.getModulePrivateSpecLoc(), >> + *TemplateId, attrs.getList(), >> + MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] >> : 0, >> + TemplateParams ? TemplateParams->size() >> : 0)); >> } >> } else if (TemplateInfo.Kind == >> ParsedTemplateInfo::ExplicitInstantiation && >> TUK == Sema::TUK_Declaration) { >> >> Modified: cfe/trunk/lib/Sema/SemaDecl.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=206442&r1=206441&r2=206442&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) >> +++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Apr 16 22:29:33 2014 >> @@ -5228,29 +5228,13 @@ Sema::ActOnVariableDeclarator(Scope *S, >> // determine whether we have a template or a template specialization. >> TemplateParams = MatchTemplateParametersToScopeSpecifier( >> D.getDeclSpec().getLocStart(), D.getIdentifierLoc(), >> - D.getCXXScopeSpec(), TemplateParamLists, >> + D.getCXXScopeSpec(), >> + D.getName().getKind() == UnqualifiedId::IK_TemplateId >> + ? D.getName().TemplateId >> + : 0, >> + TemplateParamLists, >> /*never a friend*/ false, IsExplicitSpecialization, Invalid); >> >> - if (D.getName().getKind() == UnqualifiedId::IK_TemplateId && >> - !TemplateParams) { >> - TemplateIdAnnotation *TemplateId = D.getName().TemplateId; >> - >> - // We have encountered something that the user meant to be a >> - // specialization (because it has explicitly-specified template >> - // arguments) but that was not introduced with a "template<>" (or >> had >> - // too few of them). >> - // FIXME: Differentiate between attempts for explicit >> instantiations >> - // (starting with "template") and the rest. >> - Diag(D.getIdentifierLoc(), diag::err_template_spec_needs_header) >> - << SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc) >> - << FixItHint::CreateInsertion(D.getDeclSpec().getLocStart(), >> - "template<> "); >> - IsExplicitSpecialization = true; >> - TemplateParams = TemplateParameterList::Create(Context, >> SourceLocation(), >> - SourceLocation(), >> 0, 0, >> - SourceLocation()); >> - } >> - >> if (TemplateParams) { >> if (!TemplateParams->size() && >> D.getName().getKind() != UnqualifiedId::IK_TemplateId) { >> @@ -5283,6 +5267,9 @@ Sema::ActOnVariableDeclarator(Scope *S, >> : diag::ext_variable_template); >> } >> } >> + } else { >> + assert(D.getName().getKind() != UnqualifiedId::IK_TemplateId && >> + "should have a 'template<>' for this decl"); >> > > This assert fires on some invalid inputs, for example > > template <typename> struct CT2 { > template <class U> struct X; > }; > template <typename T> int CT2<int>::X<>; > > Is the right fix just to change this to assert(Invalid || D.getNameā¦)? > (Also attached in patch form.) > > >> } >> >> if (IsVariableTemplateSpecialization) { >> @@ -6709,8 +6696,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, >> if (TemplateParameterList *TemplateParams = >> MatchTemplateParametersToScopeSpecifier( >> D.getDeclSpec().getLocStart(), D.getIdentifierLoc(), >> - D.getCXXScopeSpec(), TemplateParamLists, isFriend, >> - isExplicitSpecialization, Invalid)) { >> + D.getCXXScopeSpec(), >> + D.getName().getKind() == UnqualifiedId::IK_TemplateId >> + ? D.getName().TemplateId >> + : 0, >> + TemplateParamLists, isFriend, isExplicitSpecialization, >> + Invalid)) { >> if (TemplateParams->size() > 0) { >> // This is a function template >> >> @@ -6751,9 +6742,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, >> // This is a function template specialization. >> isFunctionTemplateSpecialization = true; >> // For source fidelity, store all the template param lists. >> - NewFD->setTemplateParameterListsInfo(Context, >> - TemplateParamLists.size(), >> - TemplateParamLists.data()); >> + if (TemplateParamLists.size() > 0) >> + NewFD->setTemplateParameterListsInfo(Context, >> + TemplateParamLists.size(), >> + >> TemplateParamLists.data()); >> >> // C++0x [temp.expl.spec]p20 forbids "template<> friend void >> foo(int);". >> if (isFriend) { >> @@ -7152,21 +7144,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, >> << SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc); >> >> HasExplicitTemplateArgs = false; >> - } else if (!isFunctionTemplateSpecialization && >> - !D.getDeclSpec().isFriendSpecified()) { >> - // We have encountered something that the user meant to be a >> - // specialization (because it has explicitly-specified template >> - // arguments) but that was not introduced with a "template<>" >> (or had >> - // too few of them). >> - // FIXME: Differentiate between attempts for explicit >> instantiations >> - // (starting with "template") and the rest. >> - Diag(D.getIdentifierLoc(), diag::err_template_spec_needs_header) >> - << SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc) >> - << FixItHint::CreateInsertion( >> - D.getDeclSpec().getLocStart(), >> - "template<> "); >> - isFunctionTemplateSpecialization = true; >> } else { >> + assert((isFunctionTemplateSpecialization || >> + D.getDeclSpec().isFriendSpecified()) && >> + "should have a 'template<>' for this decl"); >> // "friend void foo<>(int);" is an implicit specialization decl. >> isFunctionTemplateSpecialization = true; >> } >> @@ -7178,7 +7159,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, >> // friend void foo<>(int); >> // Go ahead and fake up a template id. >> HasExplicitTemplateArgs = true; >> - TemplateArgs.setLAngleLoc(D.getIdentifierLoc()); >> + TemplateArgs.setLAngleLoc(D.getIdentifierLoc()); >> TemplateArgs.setRAngleLoc(D.getIdentifierLoc()); >> } >> >> @@ -10569,8 +10550,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned >> (SS.isNotEmpty() && TUK != TUK_Reference)) { >> if (TemplateParameterList *TemplateParams = >> MatchTemplateParametersToScopeSpecifier( >> - KWLoc, NameLoc, SS, TemplateParameterLists, TUK == >> TUK_Friend, >> - isExplicitSpecialization, Invalid)) { >> + KWLoc, NameLoc, SS, 0, TemplateParameterLists, >> + TUK == TUK_Friend, isExplicitSpecialization, Invalid)) { >> if (Kind == TTK_Enum) { >> Diag(KWLoc, diag::err_enum_template); >> return 0; >> >> Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=206442&r1=206441&r2=206442&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) >> +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Apr 16 22:29:33 2014 >> @@ -11370,7 +11370,7 @@ Decl *Sema::ActOnTemplatedFriendTag(Scop >> >> if (TemplateParameterList *TemplateParams = >> MatchTemplateParametersToScopeSpecifier( >> - TagLoc, NameLoc, SS, TempParamLists, /*friend*/ true, >> + TagLoc, NameLoc, SS, 0, TempParamLists, /*friend*/ true, >> isExplicitSpecialization, Invalid)) { >> if (TemplateParams->size() > 0) { >> // This is a declaration of a class template. >> >> Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=206442&r1=206441&r2=206442&view=diff >> >> ============================================================================== >> --- cfe/trunk/lib/Sema/SemaTemplate.cpp (original) >> +++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Apr 16 22:29:33 2014 >> @@ -1593,6 +1593,9 @@ static SourceRange getRangeOfTypeInNeste >> /// parameter lists. This scope specifier precedes a qualified name that >> is >> /// being declared. >> /// >> +/// \param TemplateId The template-id following the scope specifier, if >> there >> +/// is one. Used to check for a missing 'template<>'. >> +/// >> /// \param ParamLists the template parameter lists, from the outermost >> to the >> /// innermost template parameter lists. >> /// >> @@ -1611,6 +1614,7 @@ static SourceRange getRangeOfTypeInNeste >> /// itself a template). >> TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( >> SourceLocation DeclStartLoc, SourceLocation DeclLoc, const >> CXXScopeSpec &SS, >> + TemplateIdAnnotation *TemplateId, >> ArrayRef<TemplateParameterList *> ParamLists, bool IsFriend, >> bool &IsExplicitSpecialization, bool &Invalid) { >> IsExplicitSpecialization = false; >> @@ -1830,6 +1834,7 @@ TemplateParameterList *Sema::MatchTempla >> else >> ExpectedTemplateLoc = DeclStartLoc; >> >> + // FIXME: Don't recover this way if we >> SawNonEmptyTemplateParameterList. >> Diag(DeclLoc, diag::err_template_spec_needs_header) >> << getRangeOfTypeInNestedNameSpecifier(Context, T, SS) >> << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> >> "); >> @@ -1875,12 +1880,33 @@ TemplateParameterList *Sema::MatchTempla >> continue; >> } >> } >> - >> + >> // If there were at least as many template-ids as there were template >> // parameter lists, then there are no template parameter lists >> remaining for >> // the declaration itself. >> - if (ParamIdx >= ParamLists.size()) >> + if (ParamIdx >= ParamLists.size()) { >> + if (TemplateId && !IsFriend) { >> + // FIXME: Don't recover this way if we >> SawNonEmptyTemplateParameterList. >> + // We don't have a template header for the declaration itself, but >> we >> + // should. >> + SourceLocation ExpectedTemplateLoc; >> + if (!ParamLists.empty()) >> + ExpectedTemplateLoc = ParamLists[0]->getTemplateLoc(); >> + else >> + ExpectedTemplateLoc = DeclStartLoc; >> + Diag(DeclLoc, diag::err_template_spec_needs_header) >> + << SourceRange(TemplateId->LAngleLoc, TemplateId->RAngleLoc) >> + << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> >> "); >> + IsExplicitSpecialization = true; >> + >> + // Fabricate an empty template parameter list for the invented >> header. >> + return TemplateParameterList::Create(Context, SourceLocation(), >> + SourceLocation(), 0, 0, >> + SourceLocation()); >> + } >> + >> return 0; >> + } >> >> // If there were too many template parameter lists, complain about >> that now. >> if (ParamIdx < ParamLists.size() - 1) { >> @@ -2355,6 +2381,17 @@ static bool isSameAsPrimaryTemplate(Temp >> return true; >> } >> >> +/// Convert the parser's template argument list representation into our >> form. >> +static TemplateArgumentListInfo >> +makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) { >> + TemplateArgumentListInfo TemplateArgs(TemplateId.LAngleLoc, >> + TemplateId.RAngleLoc); >> + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId.getTemplateArgs(), >> + TemplateId.NumArgs); >> + S.translateTemplateArguments(TemplateArgsPtr, TemplateArgs); >> + return TemplateArgs; >> +} >> + >> DeclResult Sema::ActOnVarTemplateSpecialization( >> Scope *S, Declarator &D, TypeSourceInfo *DI, SourceLocation >> TemplateKWLoc, >> TemplateParameterList *TemplateParams, VarDecl::StorageClass SC, >> @@ -2364,13 +2401,12 @@ DeclResult Sema::ActOnVarTemplateSpecial >> "Variable template specialization is declared with a template >> it."); >> >> TemplateIdAnnotation *TemplateId = D.getName().TemplateId; >> + TemplateArgumentListInfo TemplateArgs = >> + makeTemplateArgumentListInfo(*this, *TemplateId); >> SourceLocation TemplateNameLoc = D.getIdentifierLoc(); >> SourceLocation LAngleLoc = TemplateId->LAngleLoc; >> SourceLocation RAngleLoc = TemplateId->RAngleLoc; >> - ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), >> - TemplateId->NumArgs); >> - TemplateArgumentListInfo TemplateArgs(LAngleLoc, RAngleLoc); >> - translateTemplateArguments(TemplateArgsPtr, TemplateArgs); >> + >> TemplateName Name = TemplateId->Template.get(); >> >> // The template-id must name a variable template. >> @@ -5840,23 +5876,23 @@ Sema::ActOnClassTemplateSpecialization(S >> TagUseKind TUK, >> SourceLocation KWLoc, >> SourceLocation ModulePrivateLoc, >> - CXXScopeSpec &SS, >> - TemplateTy TemplateD, >> - SourceLocation TemplateNameLoc, >> - SourceLocation LAngleLoc, >> - ASTTemplateArgsPtr TemplateArgsIn, >> - SourceLocation RAngleLoc, >> + TemplateIdAnnotation &TemplateId, >> AttributeList *Attr, >> MultiTemplateParamsArg >> TemplateParameterLists) { >> assert(TUK != TUK_Reference && "References are not specializations"); >> >> + CXXScopeSpec &SS = TemplateId.SS; >> + >> // NOTE: KWLoc is the location of the tag keyword. This will instead >> // store the location of the outermost template keyword in the >> declaration. >> SourceLocation TemplateKWLoc = TemplateParameterLists.size() > 0 >> - ? TemplateParameterLists[0]->getTemplateLoc() : SourceLocation(); >> + ? TemplateParameterLists[0]->getTemplateLoc() : KWLoc; >> + SourceLocation TemplateNameLoc = TemplateId.TemplateNameLoc; >> + SourceLocation LAngleLoc = TemplateId.LAngleLoc; >> + SourceLocation RAngleLoc = TemplateId.RAngleLoc; >> >> // Find the class template we're specializing >> - TemplateName Name = TemplateD.get(); >> + TemplateName Name = TemplateId.Template.get(); >> ClassTemplateDecl *ClassTemplate >> = dyn_cast_or_null<ClassTemplateDecl>(Name.getAsTemplateDecl()); >> >> @@ -5877,8 +5913,9 @@ Sema::ActOnClassTemplateSpecialization(S >> bool Invalid = false; >> TemplateParameterList *TemplateParams = >> MatchTemplateParametersToScopeSpecifier( >> - TemplateNameLoc, TemplateNameLoc, SS, TemplateParameterLists, >> - TUK == TUK_Friend, isExplicitSpecialization, Invalid); >> + KWLoc, TemplateNameLoc, SS, &TemplateId, >> + TemplateParameterLists, TUK == TUK_Friend, >> isExplicitSpecialization, >> + Invalid); >> if (Invalid) >> return true; >> >> @@ -5929,11 +5966,8 @@ Sema::ActOnClassTemplateSpecialization(S >> << SourceRange(LAngleLoc, RAngleLoc); >> else >> isExplicitSpecialization = true; >> - } else if (TUK != TUK_Friend) { >> - Diag(KWLoc, diag::err_template_spec_needs_header) >> - << FixItHint::CreateInsertion(KWLoc, "template<> "); >> - TemplateKWLoc = KWLoc; >> - isExplicitSpecialization = true; >> + } else { >> + assert(TUK == TUK_Friend && "should have a 'template<>' for this >> decl"); >> } >> >> // Check that the specialization uses the same tag kind as the >> @@ -5953,10 +5987,8 @@ Sema::ActOnClassTemplateSpecialization(S >> } >> >> // Translate the parser's template argument list in our AST format. >> - TemplateArgumentListInfo TemplateArgs; >> - TemplateArgs.setLAngleLoc(LAngleLoc); >> - TemplateArgs.setRAngleLoc(RAngleLoc); >> - translateTemplateArguments(TemplateArgsIn, TemplateArgs); >> + TemplateArgumentListInfo TemplateArgs = >> + makeTemplateArgumentListInfo(*this, TemplateId); >> >> // Check for unexpanded parameter packs in any of the template >> arguments. >> for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I) >> @@ -7416,13 +7448,8 @@ DeclResult Sema::ActOnExplicitInstantiat >> } >> >> // Translate the parser's template argument list into our AST >> format. >> - TemplateArgumentListInfo TemplateArgs; >> - TemplateIdAnnotation *TemplateId = D.getName().TemplateId; >> - TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc); >> - TemplateArgs.setRAngleLoc(TemplateId->RAngleLoc); >> - ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), >> - TemplateId->NumArgs); >> - translateTemplateArguments(TemplateArgsPtr, TemplateArgs); >> + TemplateArgumentListInfo TemplateArgs = >> + makeTemplateArgumentListInfo(*this, *D.getName().TemplateId); >> >> DeclResult Res = CheckVarTemplateId(PrevTemplate, TemplateLoc, >> D.getIdentifierLoc(), >> TemplateArgs); >> @@ -7492,12 +7519,7 @@ DeclResult Sema::ActOnExplicitInstantiat >> bool HasExplicitTemplateArgs = false; >> TemplateArgumentListInfo TemplateArgs; >> if (D.getName().getKind() == UnqualifiedId::IK_TemplateId) { >> - TemplateIdAnnotation *TemplateId = D.getName().TemplateId; >> - TemplateArgs.setLAngleLoc(TemplateId->LAngleLoc); >> - TemplateArgs.setRAngleLoc(TemplateId->RAngleLoc); >> - ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), >> - TemplateId->NumArgs); >> - translateTemplateArguments(TemplateArgsPtr, TemplateArgs); >> + TemplateArgs = makeTemplateArgumentListInfo(*this, >> *D.getName().TemplateId); >> HasExplicitTemplateArgs = true; >> } >> >> >> Modified: cfe/trunk/test/FixIt/fixit.cpp >> URL: >> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/fixit.cpp?rev=206442&r1=206441&r2=206442&view=diff >> >> ============================================================================== >> --- cfe/trunk/test/FixIt/fixit.cpp (original) >> +++ cfe/trunk/test/FixIt/fixit.cpp Wed Apr 16 22:29:33 2014 >> @@ -19,7 +19,7 @@ virtual void C1::f() { } // expected-err >> >> static void C1::g() { } // expected-error{{'static' can only be >> specified inside the class definition}} >> >> -template<int Value> struct CT { }; // expected-note{{previous use is >> here}} >> +template<int Value> struct CT { template<typename> struct Inner; }; // >> expected-note{{previous use is here}} >> >> CT<10 >> 2> ct; // expected-warning{{require parentheses}} >> >> @@ -32,6 +32,8 @@ struct CT<0> { }; // expected-error{{'te >> >> template<> union CT<1> { }; // expected-error{{tag type}} >> >> +struct CT<2>::Inner<int> { }; // expected-error 2{{'template<>'}} >> + >> // Access declarations >> class A { >> protected: >> >> >> _______________________________________________ >> cfe-commits mailing list >> [email protected] >> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits >> > >
_______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
