1 #include "XYCheck.h"
2 
3 #include <clang/AST/Decl.h>
4 #include <clang/AST/DeclCXX.h>
5 #include <clang/ASTMatchers/ASTMatchFinder.h>
6 #include <clang/ASTMatchers/ASTMatchers.h>
7 #include <clang/ASTMatchers/ASTMatchersInternal.h>
8 #include <clang/Basic/Diagnostic.h>
9 #include <clang/Basic/DiagnosticIDs.h>
10 #include <clang/Basic/Specifiers.h>
11 
12 #include "Utils.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang
17 {
18 namespace tidy
19 {
20 namespace cata
21 {
22 
registerMatchers(MatchFinder * Finder)23 void XYCheck::registerMatchers( MatchFinder *Finder )
24 {
25     Finder->addMatcher(
26         fieldDecl(
27             hasType( isInteger() ),
28             isXParam(),
29             hasParent(
30                 cxxRecordDecl(
31                     forEachDescendant( fieldDecl( isYParam() ).bind( "yfield" ) )
32                 ).bind( "record" )
33             )
34         ).bind( "xfield" ),
35         this
36     );
37     Finder->addMatcher(
38         parmVarDecl(
39             hasType( isInteger() ),
40             isXParam(),
41             hasAncestor( functionDecl().bind( "function" ) )
42         ).bind( "xparam" ),
43         this
44     );
45 }
46 
CheckField(XYCheck & Check,const MatchFinder::MatchResult & Result)47 static void CheckField( XYCheck &Check, const MatchFinder::MatchResult &Result )
48 {
49     const FieldDecl *XVar = Result.Nodes.getNodeAs<FieldDecl>( "xfield" );
50     const FieldDecl *YVar = Result.Nodes.getNodeAs<FieldDecl>( "yfield" );
51     const CXXRecordDecl *Record = Result.Nodes.getNodeAs<CXXRecordDecl>( "record" );
52     if( !XVar || !YVar || !Record ) {
53         return;
54     }
55     const NameConvention NameMatcher( XVar->getName() );
56     if( !NameMatcher ) {
57         return;
58     }
59     if( NameMatcher.Match( YVar->getName() ) != NameConvention::YName ) {
60         return;
61     }
62 
63     const FieldDecl *ZVar = nullptr;
64     for( FieldDecl *Field : Record->fields() ) {
65         if( NameMatcher.Match( Field->getName() ) == NameConvention::ZName ) {
66             ZVar = Field;
67             break;
68         }
69     }
70     TemplateSpecializationKind tsk = Record->getTemplateSpecializationKind();
71     if( tsk != TSK_Undeclared ) {
72         // Avoid duplicate warnings for specializations
73         return;
74     }
75     if( ZVar ) {
76         Check.diag(
77             Record->getLocation(),
78             "%0 defines fields %1, %2, and %3.  Consider combining into a single tripoint "
79             "field." ) << Record << XVar << YVar << ZVar;
80     } else {
81         Check.diag(
82             Record->getLocation(),
83             "%0 defines fields %1 and %2.  Consider combining into a single point "
84             "field." ) << Record << XVar << YVar;
85     }
86     Check.diag( XVar->getLocation(), "declaration of %0", DiagnosticIDs::Note ) << XVar;
87     Check.diag( YVar->getLocation(), "declaration of %0", DiagnosticIDs::Note ) << YVar;
88     if( ZVar ) {
89         Check.diag( ZVar->getLocation(), "declaration of %0", DiagnosticIDs::Note ) << ZVar;
90     }
91 }
92 
CheckParam(XYCheck & Check,const MatchFinder::MatchResult & Result)93 static void CheckParam( XYCheck &Check, const MatchFinder::MatchResult &Result )
94 {
95     const ParmVarDecl *XParam = Result.Nodes.getNodeAs<ParmVarDecl>( "xparam" );
96     const FunctionDecl *Function = Result.Nodes.getNodeAs<FunctionDecl>( "function" );
97     if( !XParam || !Function ) {
98         return;
99     }
100     // Don't mess with the methods of point and tripoint themselves
101     if( isPointMethod( Function ) ) {
102         return;
103     }
104     const NameConvention NameMatcher( XParam->getName() );
105 
106     const ParmVarDecl *YParam = nullptr;
107     const ParmVarDecl *ZParam = nullptr;
108     for( ParmVarDecl *Parameter : Function->parameters() ) {
109         switch( NameMatcher.Match( Parameter->getName() ) ) {
110             case NameConvention::ZName:
111                 ZParam = Parameter;
112                 break;
113             case NameConvention::YName:
114                 YParam = Parameter;
115                 break;
116             default:
117                 break;
118         }
119     }
120 
121     if( !YParam ) {
122         return;
123     }
124 
125     TemplateSpecializationKind tsk = Function->getTemplateSpecializationKind();
126     if( tsk != TSK_Undeclared ) {
127         // Avoid duplicate warnings for specializations
128         return;
129     }
130 
131     if( ZParam ) {
132         Check.diag(
133             Function->getLocation(),
134             "%0 has parameters %1, %2, and %3.  Consider combining into a single tripoint "
135             "parameter." ) << Function << XParam << YParam << ZParam;
136     } else {
137         Check.diag(
138             Function->getLocation(),
139             "%0 has parameters %1 and %2.  Consider combining into a single point "
140             "parameter." ) << Function << XParam << YParam;
141     }
142     Check.diag( XParam->getLocation(), "declaration of %0", DiagnosticIDs::Note ) << XParam;
143     Check.diag( YParam->getLocation(), "declaration of %0", DiagnosticIDs::Note ) << YParam;
144     if( ZParam ) {
145         Check.diag( ZParam->getLocation(), "declaration of %0", DiagnosticIDs::Note ) << ZParam;
146     }
147 }
148 
check(const MatchFinder::MatchResult & Result)149 void XYCheck::check( const MatchFinder::MatchResult &Result )
150 {
151     CheckField( *this, Result );
152     CheckParam( *this, Result );
153 }
154 
155 } // namespace cata
156 } // namespace tidy
157 } // namespace clang
158