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