1 #include "JsonTranslationInputCheck.h"
2 
3 #include <clang/AST/Decl.h>
4 #include <clang/AST/Expr.h>
5 #include <clang/ASTMatchers/ASTMatchFinder.h>
6 #include <clang/ASTMatchers/ASTMatchers.h>
7 #include <clang/ASTMatchers/ASTMatchersInternal.h>
8 #include <clang/Basic/DiagnosticIDs.h>
9 
10 using namespace clang::ast_matchers;
11 
12 namespace clang
13 {
14 namespace tidy
15 {
16 namespace cata
17 {
18 
registerMatchers(MatchFinder * Finder)19 void JsonTranslationInputCheck::registerMatchers( MatchFinder *Finder )
20 {
21     // <translation function>( ... <json input object>.<method>(...) ... )
22     Finder->addMatcher(
23         callExpr(
24             // <json input object>.<method>
25             callee( cxxMethodDecl(
26                         // <json input object>
27                         ofClass( hasAnyName( "JsonIn", "JsonArray", "JsonObject" ) )
28                     ) ),
29             // <translation function>( ... )
30             hasAncestor(
31                 callExpr( callee( decl( anyOf(
32                                             functionDecl(
33                                                     hasAnyName( "_", "gettext", "pgettext", "ngettext", "npgettext" )
34                                             ).bind( "translationFunc" ),
35                                             functionDecl(
36                                                     hasAnyName( "to_translation", "pl_translation" )
37                                             ) ) ) )
38                           // no_translation is ok, it's used to load generated names such as artifact names
39                         ).bind( "translationCall" ) )
40         ).bind( "jsonInputCall" ),
41         this
42     );
43 }
44 
check(const MatchFinder::MatchResult & Result)45 void JsonTranslationInputCheck::check( const MatchFinder::MatchResult &Result )
46 {
47     const CallExpr *jsonInputCall = Result.Nodes.getNodeAs<CallExpr>( "jsonInputCall" );
48     const CallExpr *translationCall = Result.Nodes.getNodeAs<CallExpr>( "translationCall" );
49     if( jsonInputCall && translationCall ) {
50         const FunctionDecl *translationFunc = Result.Nodes.getNodeAs<FunctionDecl>( "translationFunc" );
51         if( translationFunc ) {
52             diag(
53                 translationCall->getBeginLoc(),
54                 "immediately translating a value read from json causes translation "
55                 "updating issues.  Consider reading into a translation object instead."
56             );
57             diag(
58                 jsonInputCall->getBeginLoc(),
59                 "value read from json",
60                 DiagnosticIDs::Note
61             );
62         } else {
63             diag(
64                 translationCall->getBeginLoc(),
65                 "read translation directly instead of constructing it from "
66                 "json strings to ensure consistent format in json."
67             );
68             diag(
69                 jsonInputCall->getBeginLoc(),
70                 "string read from json",
71                 DiagnosticIDs::Note
72             );
73         }
74     }
75 }
76 
77 } // namespace cata
78 } // namespace tidy
79 } // namespace clang
80